Typescript 2.4 引入了弱类型(weak types)的概念。当一个类型的所有属性都是可选的时候 ,它被认为是弱类型。说得更具体一些,一个弱类型定义了一个或多个可选属性,没有必需属性,也没有索引签名。
举个例子,下面的类型被认为是弱类型:
interface PrettierConfig {
printWidth?: number;
tabWidth?: number;
semi?: boolean;
}
弱类型检测的主要目的是为了找出代码中可能的错误,避免成为潜在的 bug。看下面的例子:
interface PrettierConfig {
printWidth?: number;
tabWidth?: number;
semi?: boolean;
}
function createFormatter(config: PrettierConfig) {
// ...
}
const prettierConfig = {
semicolons: true,
};
const formatter = createFormatter(prettierConfig); // Error
在 Typescript 2.4 之前,上面的这段代码是类型正确的。PrettierConfig
的所有属性都是可选的,所以一个属性都不设置也没问题。并且,我们的 pretteirConfig
对象有一个 semicolons
属性,这个属性并不存在于 PrettierConfig
类型中。
从 Typescript 2.4 开始,如果给弱类型赋值的对象中没有和弱类型相互重叠的属性(看文档),类型检查器会报以下的错误:
Type '{ semicolons: boolean; }' has no properties
in common with type 'PrettierConfig'.
虽然我们的代码严格说并没有错,但它很可能潜入了一个 bug。createFormatter
函数很可能会忽略 config
中它不认识的任何属性(比如 semicolons
),而只使用它认识属性的默认值。在这种情况下,我们的 semicolons
属性不会有任何作用,无论它被设置为 true
或者 false
。
Typescript 弱类型检测能帮助我们找出函数调用中 prettierConfig
可能存在的问题。如此,我们就可以更早地知道某些潜在的问题。
除了依赖弱类型检测,我们可以显式地给 prettierConfig
对象添加类型标注:
const prettierConfig: PrettierConfig = {
semicolons: true, // Error
};
const formatter = createFormatter(prettierConfig);
添加了显式标注后,我们得到了以下的类型错误:
Object literal may only specify known properties,
and 'semicolons' does not exist in type 'PrettierConfig'.
通过这种方式 ,类型错误会更就近提示。它会提示在我们错误定义 semicolons
属性的地方,而不是正确地将 prettierConfig
参数传给 createFormatter
函数的时候。
这样做另一个好处是,Typescript 会提供自动补全的建议,因为类型标注告知了我们创建的对象类型。
假如,出于某些原因,我们不希望对某个特定的弱类型检测并报错,该怎么做?一种方法是给 PrettierConfig
添加 unknown
类型的索引签名:
interface PrettierConfig {
[prop: string]: unknown;
printWidth?: number;
tabWidth?: number;
semi?: boolean;
}
function createFormatter(config: PrettierConfig) {
// ...
}
const prettierConfig = {
semicolons: true,
};
const formatter = createFormatter(prettierConfig);
现在,这段代码是类型正确的了,因为我们显式地允许 PrettierConfig
类型中有未知的属性。
或者,我们也可以使用类型断言来告诉类型检查器我们的 prettierConfig
对象是 PrettierConfig
类型:
interface PrettierConfig {
printWidth?: number;
tabWidth?: number;
semi?: boolean;
}
function createFormatter(config: PrettierConfig) {
// ...
}
const prettierConfig = {
semicolons: true,
};
const formatter = createFormatter(prettierConfig as PrettierConfig);
我建议你避免使用类型断言来静默弱类型检测。可能是有一些场景使用类型断言是合适的,但一般而言,你应该选择其他更好的解决方案。
注意弱类型检测只会在完全没有属性重叠的情况下报类型错误。只要你设置了一个或多个弱类型中定义的属性,编译器将不会再报错误:
interface PrettierConfig {
printWidth?: number;
tabWidth?: number;
semi?: boolean;
}
function createFormatter(config: PrettierConfig) {
// ...
}
const prettierConfig = {
printWidth: 100,
semicolons: true,
};
const formatter = createFormatter(prettierConfig);
在上面的例子中,我同时设置了 printWidth
和 semicolons
。因为 printWidth
存在于 PrettierConfig
中,所以对象和 PrettierConfig
类型有属性重叠,弱类型的检测不再对函数调用提示错误。