繁体   English   中英

泛型打字稿,为什么这段代码会抛出错误 Type '{}' cannot be assigned to a type?

[英]Generics typescript, why does this code throw an error Type '{}' cannot be assigned to a type?

我不明白为什么这段代码会抛出错误。 - 类型“{}”不可分配给类型“Keys<T>”。

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
}

您可以在此处查看代码

我希望通用代码可以正常工作

因为

const foo = <T = "a" | "b">()=>{
    const a: {a: string, b: string} = {} // <- incompatible
    return a
}

TLDR:Typescript 对待像string这样的宽属性类型比像'a' | 'b' | 'c'这样的属性类型要宽容得多。 'a' | 'b' | 'c' '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' '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 ,而不是无限宽的string | symbol string | symbol 所以它应用了更严格的类型检查。

Typescript 是对的。 您的代码根本不分配任何属性,但类型表明这应该有效:

foo<{ a: number }>().a // number

但是您的代码永远不会分配该属性,因此您将在运行时得到undefined并且可能会导致其他东西崩溃。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM