如上所述,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吗?
答案 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错误。如果您添加number
并string
可以投放到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);
}
}