由于弱类型检查和缺少运行时类型检查的代码生成,因此在typescript中导致错误的结果

时间:2016-04-27 11:48:49

标签: typescript

如上所述,typescript可以启用静态类型,如您所知,编译器中的类型检查是通过早期绑定或后期绑定范例进行的。我认为typescript的反编译器使用类似早期绑定样式的东西。我们知道,为了完成类型检查,编译器应该为生成的对象添加一些额外的代码,以便在运行时检查类型。正如我所看到的,打字稿并没有为运行时检查类型添加额外的js代码,这种风格给我们的团队带来了一些问题。例如,在下面的代码中,调用m方法的逻辑结果应该是1312或者像java中的ClassCastException这样的异常。但结果是别的:

function m(a:string){
    console.log(a+12)
}

var a:any=13
m(a as string) // result is 25

由于这个问题,我应该用js替换项目中的typescript吗?

3 个答案:

答案 0 :(得分:3)

Typescript不是一个库,它是一种语言,它没有额外的功能(只是pollyfil在这里和那里),因为在运行时没有类型强制执行。

话虽如此,你的例子对打字稿编纂并不公平,因为如果你这样做:

var a: number = 13;
m(a as string);

您收到错误消息:Neither type 'number' nor type 'string' is assignable to the other 即使有一个更复杂的例子:

interface A {
    a: number;
}

interface B {
    b: string;
}

function echoNumber(a: A): number {
    return a.a;
}

console.log(echoNumber(<A> { b : "str" }));

你得到同样的错误。

所以打字稿起作用了,效果很好,但是你没有正确使用它(至少在你的例子中),因为如果你使用any那么你也可以直接在javascript中工作,整个打字稿的要点是any的类型而不是使用(仅当你必须时)。

答案 1 :(得分:1)

不幸的是,对于这些情况,在将TypeScript编译为Javascript时会强调效率。也就是说,输出中省略了不一定必要的代码 - 例如,__extend__decorate函数。虽然谎言到编译器不是应该处理的事情,但运行时类型错误有时也可能来自真实的用例。

但是,我发现这是使所有保留TypeScript提供的优势的一个弱理由,其中一个恰好是这种运行时类型不匹配的优雅解决方案:decorators。特别是与reflect-metadata结合使用时。

如果要将m作为类的方法包含,可以使用method decorator来确保在运行时传递了正确类型的值:

function TypeCheck(
    target: any,
    propertyKey: string,
    propertyDescriptor: PropertyDescriptor
): any {
    var originalMethod = target[propertyKey].bind(target);
    var paramTypes = Reflect.getMetadata("design:paramtypes", target, propertyKey);

    // Return a new property descriptor for the decorated method.
    return {
        value: function () {
            // Check argument types.
            for (var i = 0; i < arguments.length; i++) {
                switch (paramTypes[i]) {
                    case String:
                        if (!(arguments[i] instanceof paramTypes[i]) && typeof arguments[i] !== "string") {
                            throw new TypeError(`Expected argument ${i} to be of type 'string'.`);
                        }
                        break;

                    case Number:
                        if (!(arguments[i] instanceof paramTypes[i]) && typeof arguments[i] !== "number") {
                            throw new TypeError(`Expected argument ${i} to be of type 'number'.`);
                        }
                        break;

                    case Boolean:
                        if (!(arguments[i] instanceof paramTypes[i]) && typeof arguments[i] !== "boolean") {
                            throw new TypeError(`Expected argument ${i} to be of type 'boolean'.`);
                        }
                        break;

                    default:
                        if (!(arguments[i] instanceof paramTypes[i])) {
                            throw new TypeError(`Expected argument ${i} to be of type '${paramTypes[i].prototype.toString.call(paramTypes[i]).toString()}'.`);
                        }
                        break;
                }

            }

            // Call original method.
            return originalMethod.apply(target, arguments);
        }
    };
}
class Foo {
    @TypeCheck
    public m(a: string) {
        console.log(a + 12);
    }
}

var foo = new Foo();
foo.m(13 as any); // Will throw TypeError.

IMO,这提供了最优雅的解决方案,但需要额外的一次性设置。是的,TypeCheck装饰器看起来很臃肿,但有些类型只需要额外的检查,因为:

"some string" instanceof String; // false

...并反映元数据也将提供包装对象作为基元的类型信息。

答案 2 :(得分:-1)

您将.toString()as string混淆。

您获得的错误与TypeScript无关。这是一个JavaScript错误。如果您添加numberstring可以投放到number,则JavaScript会将string投射到number并添加它们。

只需使用正确的类型即可解决此问题:

function m(a: string){
    console.log(a + "12")
}

var a: string = "13".
m(a) // result is 1312

var b = 13.toString();
m(b) // result is 1312

var c = 13;
m(b.toString()) // result is 1312

var d = 13;
m(d) // Combile Error

var e = "13";
m(3) // result is 1312

此外,您可以使用JavaScript运行时类型检查以及Typescript类型保护。

function(a: string | number) {
    If (typeof a == 'number') {
         console.log('13' + a.toString());
    } else {
         console.log('13' + a);
    }
}