泛型打字稿,为什么这段代码抛出一个错误,类型'{}'不能分配给一个类型?



我不明白为什么这段代码会抛出错误。-类型"{}"不能赋值给类型"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
}

最新更新