在对象字面量中使用动态键时,我对 TypeScript 如何执行类型检查感到有些困惑。考虑以下两个返回对象副本的函数:
type Foo = {
a: number;
b: number;
};
const INIT_FOO: Foo = { a: 0, b: 0 };
function test1(k: keyof Foo) {
const f: Foo = { ...INIT_FOO, [k]: true };
return f
}
function test2(k: keyof Foo) {
const f: Foo = { ...INIT_FOO };
f[k] = true;
return f
}
Run Code Online (Sandbox Code Playgroud)
TypeScript 编译器只会报告函数的错误test2,而不是函数test1。
test1当函数明显不正确时,为什么编译器不报告错误?
这是一个已知问题,已在microsoft/TypeScript#38663中报告。发生这种情况是因为其中键是字符串文字的并集(例如keyof Foo)的计算属性被扩展为string索引签名。它并不完全错误,但不够具体,没有什么用处。此行为要么是错误(如microsoft/TypeScript#13948中报告) ,要么可能是设计限制(如microsoft/TypeScript#21030中所述) 。无论如何,这就是该语言当前的行为。
一旦计算属性扩展到索引签名,就不可能捕获该错误。索引签名不被视为多余属性;类型{a: number, b: number} & {[k: string]: boolean}(大约是您在此处获得的类型)可分配给{a: number, b: number},因此不会报告任何错误。但当然,实际上,您所做的是将 a 分配给或属性boolean之一;只是由于索引签名变宽,编译器看不到它。ab
除了记下这个问题并小心之外,您可以做的另一件事是手动实现一个函数,该函数生成当您拥有键类型联合的计算属性时“应该”生成的类型:
function computedProp<K extends PropertyKey, V>(
key: K, val: V
): K extends any ? { [P in K]: V } : never {
return { [key]: val } as any;
}
Run Code Online (Sandbox Code Playgroud)
返回值是一个分布式条件类型,它生成对象类型的联合,如下所示:
const example = computedProp(Math.random() < 0.5 ? "a" : "b", true);
// const example: { a: boolean; } | { b: boolean; }
Run Code Online (Sandbox Code Playgroud)
联合{a: boolean} | {b: boolean}是 类型的更准确表示{[k]: true}。如果我们使用它,您会得到预期的错误:
function test1(k: keyof Foo) {
const f: Foo = { ...INIT_FOO, ...computedProp(k, true) }; // error!
// ~
// '{ a: boolean; b: number; } | { b: boolean; a: number; }' is not assignable to 'Foo'.
return f
}
Run Code Online (Sandbox Code Playgroud)
显然,使用函数而不是直接计算的属性并不是真正理想的,但至少您可以维护类型安全(如果它对您很重要)。
| 归档时间: |
|
| 查看次数: |
236 次 |
| 最近记录: |