为什么TypeScript在从对象中查找属性时不检查未定义?

时间:2018-02-09 12:33:03

标签: typescript

TypeScript允许为可以包含任何属性的对象定义接口:

interface NumberHash {
    [key: string]: number;
}

let numbers: NumberHash = {
    zero: 0,
    one: 1,
    pi: 3.14
};

从这样的对象中查找属性时,它们会按预期返回number

let key = 'pi';
let pi = numbers[key];

console.log(pi.toFixed(1));  // all is well

但是,在查找未知属性时,它们仍会以number的形式返回,即使它们实际上是undefined

let key = 'foo';
let foo = numbers[key];  // `foo` becomes `undefined`

console.log(foo.toFixed(1));  // errors at runtime

上面的代码在运行时抛出TypeError,但在编译期间不抛出。为什么TypeScript在编译期间没有捕获到这个错误?

2 个答案:

答案 0 :(得分:1)

使用strictNullChecks编译器选项,接口NumberHash可以声明如下:

interface NumberHash {
    [key: string]: number | undefined;
}

然后:

let numbers: NumberHash = {};
let foo = numbers['foo'];

console.log(foo.toFixed(1));  // error at compile time: Object is possibly 'undefined'

另见:

答案 1 :(得分:0)

在您的示例中,很清楚NumberHash对象上将存在和不存在哪些属性 - 它们是在分配给numbers变量的对象文字中明确指定的。认为这是TypeScript在编译时可以防范的事情是合理的。

然而,采取一个更动态的例子:

interface NumberHash {
    [key: string]: number;
}

let numbers: NumberHash = {};

const max = Math.random() * 100;
for (var i = 0; i < max; i++) {
    numbers[i.toString()] = i;
}

let foo = numbers["42"];

// is foo undefined?  Who knows! Depends
// on what Math.random() returned.
console.log(foo.toFixed(1));   

在编译时,编译器无法知道Math.random()在运行时将返回什么。因为编译器必须同时处理你的例子和我的例子,所以它由开发人员决定是否存在密钥。

如果您知道编译时NumberHash对象中将存在的属性,则可以将此数据结构重写为类:

class Numbers {
    public static readonly zero: number = 0;
    public static readonly one: number = 1;
    public static readonly pi: number = Math.PI;
}

// both of these will compile without errors
let foo = Numbers['zero'];
let bar = Numbers.one;

// Compiler error: Element implicitly has an 'any' type 
// because type 'typeof Numbers' has no index signature.
let baz = Numbers['foo'];