我不明白为什么这段代码会抛出错误。-类型"{}"不能赋值给类型"keyys
type Keys<T extends string|symbol>={
[key in T]: string;
};
const foo = <T extends string|symbol>()=>{
const a:Keys<T> = {}
return a
}
此外,如果您手动替换字符串或符号类型,则不会收到任何错误。只是一个警告,T是为函数声明的,但没有使用。
工作代码示例:
type Keys<T extends string|symbol>={
[key in T]: string;
};
const foo = <T extends string|symbol>()=>{
const a:Keys<string> = {}
return a
}
type Keys<T extends string|symbol>={
[key in T]: string;
};
const foo = <T extends string|symbol>()=>{
const a:Keys<symbol> = {}
return a
}
你可以在这里查看代码
我期望泛型代码工作良好
TLDR: Typescript对待像string
这样的宽属性类型要比'a' | 'b' | 'c'
这样的属性类型宽松得多。
首先,您的Keys
类型只是内置的Record
类型,定义为:
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
为了简单起见,我将使用这个
为什么会这样呢?
function foo<T extends string | symbol>() {
const foo: Record<string, string> = {} // fine
const bar: Record<T, string> = {} // error
}
答案是,当键类型为无限时,Typescript的行为与键类型为有限时不同。
string
可以是任何字符串,该字符串不被跟踪。'a' | 'b' | 'c'
是有限字符串列表。
Typescript不会强制存在无限键,因为它做不到。该类型表示任何字符串都返回一个值,Typescript允许你这样使用它。
这确实会导致一些问题:
const obj: Record<string, string> = { a: 'test' }
obj.b.toUpperCase() // no type error, crash at runtime
更好的格式是:
const obj: Record<string, string | undefined> = { a: 'test' }
obj.b.toUpperCase() // type error
obj.b?.toUpperCase() // fine
这里的值类型可能是undefined
,这意味着我们必须确保每个属性在将其视为string
之前都有一个值。这就把类型安全还给了我们。
但是当编译器可以知道键时,它可以强制执行这些键,并且类型检查变得更加严格:
const obj: Record<'a', string> = { a: 'test' }
obj.b.toUpperCase() // type error
因为它现在有足够的信息来应用更强的类型,
这是怎么回事:
const foo = <T extends string|symbol>()=>{
const a: Record<T, string> = {} // type error
return a
}
是Typescript认为T
可能会被推断为string | symbol
的有限子集,而不是string | symbol
的无限宽类型。所以它应用了更严格的类型检查。
Typescript是对的。您的代码根本没有分配任何属性,但类型说明这应该可以工作:
foo<{ a: number }>().a // number
但是你的代码从来没有分配过这个属性,所以你会在运行时得到undefined
,这可能会导致其他东西崩溃。
因为
const foo = <T = "a" | "b">()=>{
const a: {a: string, b: string} = {} // <- incompatible
return a
}