简体   繁体   English

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

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

I can't figure out why this code is throwing an error.我不明白为什么这段代码会抛出错误。 - Type '{}' is not assignable to type 'Keys<T>'. - 类型“{}”不可分配给类型“Keys<T>”。

type Keys<T extends string|symbol>={
    [key in T]: string;
};
const foo = <T extends string|symbol>()=>{
    const a:Keys<T> = {}
    return a
}

Moreover, if you manually substitute the string or symbol type, no errors will be received.此外,如果您手动替换字符串或符号类型,则不会收到任何错误。 Just a warning that T is declared for a function but not used.只是警告 T 是为函数声明但未使用的。

Example of working codes:工作代码示例:

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
}

You can check the code here您可以在此处查看代码

I expected generic code to work fine我希望通用代码可以正常工作

Because因为

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

TLDR: Typescript treats wide property types like string much more leniently than property types like 'a' | 'b' | 'c' TLDR:Typescript 对待像string这样的宽属性类型比像'a' | 'b' | 'c'这样的属性类型要宽容得多。 'a' | 'b' | 'c' 'a' | 'b' | 'c' . 'a' | 'b' | 'c'


First of all, your Keys type is just the built in Record type which is defined as:首先,您的Keys类型只是内置的Record类型,定义为:

type Record<K extends string | number | symbol, T> = { [P in K]: T; }

So I'm going to use that instead for simplicity.所以为了简单起见,我将使用它。


So why is this the case?那么为什么会这样呢?

function foo<T extends string | symbol>() {
  const foo: Record<string, string> = {} // fine
  const bar: Record<T, string> = {} // error
}

The answer is because Typescript behaves differently when a key type is infinite, versus when the key type is finite.答案是因为 Typescript 在键类型为无限时与键类型为有限时的行为不同。

  • string could be any string, which string is not tracked. string可以是任何字符串,不跟踪哪个字符串。
  • 'a' | 'b' | 'c' 'a' | 'b' | 'c' is a finite list of strings. 'a' | 'b' | 'c'是一个有限的字符串列表。

Typescript doesn't bother enforcing the existence of infinite keys because it can't. Typescript 不会强制执行无限键的存在,因为它不能。 The type says that any string returns a value, Typescript lets you use it like that.该类型表示任何字符串都会返回一个值,Typescript 允许您这样使用它。

And this does cause some problems:这确实会导致一些问题:

const obj: Record<string, string> = { a: 'test' }
obj.b.toUpperCase() // no type error, crash at runtime

A better type for that would be:一个更好的类型是:

const obj: Record<string, string | undefined> = { a: 'test' }
obj.b.toUpperCase() // type error
obj.b?.toUpperCase() // fine

Here the value type might be undefined which means we would have to make sure each property has a value before we treat it as a string .这里的值类型可能是undefined的,这意味着我们必须确保每个属性在我们将其视为string之前都有一个值。 This gives us type safety back.这给了我们类型安全性。

But when the compiler can know the keys , then it can enforce those keys, and the type checking gets much stricter:但是当编译器可以知道键时,它就可以强制执行这些键,并且类型检查变得更加严格:

const obj: Record<'a', string> = { a: 'test' }
obj.b.toUpperCase() // type error

Since this now has enough information to apply stronger types, it does.因为它现在有足够的信息来应用更强的类型,所以它确实如此。


So what's going here:那么这里发生了什么:

const foo = <T extends string|symbol>()=>{
    const a: Record<T, string> = {} // type error
    return a
}

Is that Typescript thinks that T is probably going to be inferred as a finite subset of string | symbol Typescript 是否认为T可能会被推断为string | symbol的有限子集? string | symbol , and not the infinite wider type of string | symbol string | symbol ,而不是无限宽的string | symbol string | symbol . string | symbol So it applies the stricter type checking.所以它应用了更严格的类型检查。

And Typescript is right. Typescript 是对的。 Your code does not assign any properties at all, but the types says that this should work:您的代码根本不分配任何属性,但类型表明这应该有效:

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

But your code doesn't ever assign that property so you will get undefined at runtime and that will probably crash something else.但是您的代码永远不会分配该属性,因此您将在运行时得到undefined并且可能会导致其他东西崩溃。

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

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