Why can't an interface be assigned to Record<string, unknown>?

Ste*_*lla 5 types compiler-errors object type-conversion typescript

I just noticed that an interface cannot be assigned to Record<string, unknown> (playground link):

interface Foo {
    foo: number
}

const foo: Foo = { foo: 1 }
const bar: Record<string, unknown> = foo
//    |-> Error: Type 'Foo' is not assignable to type 'Record<string, unknown>'
//               Index signature is missing in type 'Foo'.(2322)
Run Code Online (Sandbox Code Playgroud)

However the same is possible, when the type declaration for Foo is omitted (playground link):

const foo = { foo: 1 }
const bar: Record<string, unknown> = foo // no error here
Run Code Online (Sandbox Code Playgroud)

Question: Why is there a difference between both examples? For me the reduced type of the variable foo is the same in both examples... Shouldn't the interface Foo be assignable to Record<string, unknwon>?!

In my understanding Record<string, unknown> is equivalent to object and thus any interface should be assignable to it. Also https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md suggests using Record<string, unknown> instead of object.

Remarks: The first example works when either object (playground link) or Record<string, any> (playground link) is used instead of Record<string, unknown>.

Les*_*iak 7

您遇到过类型中缺少索引签名(仅在接口上,而不是在类型别名上)#15300

当您将接口更改为类型时,代码将起作用:

type Foo = {
    foo: number
}

const foo: Foo = { foo: 1 };
const bar: Record<string, unknown> = foo;
Run Code Online (Sandbox Code Playgroud)

  • 哇,这太违反直觉了。 (3认同)

D M*_*D M 5

编辑:@Lesiak 上面有正确的答案。我将其保留只是为了相关答案的链接。

诚然,我有点超出了我的深度,但我正在查看这个答案,我发现:

[A] TypeScript 安全性的很大一部分来自于这样一个事实:[...] 只有当它知道它明确打算作为字典时,它才会让您将对象视为字典。

这与我的测试一致。修改您的接口以显式地将Foo.foo其视为索引不会产生错误。(游乐场链接

interface Foo {
    [foo: string]: number
}

const foo = { foo: 1 }
const bar: Record<string, unknown> = foo
Run Code Online (Sandbox Code Playgroud)

这并不能完全回答您的问题,因为Record<string, any>与您的显式界面一起工作,但也许知识渊博的人可以从这里获取它。

  • 实际上 `interface Foo { foo: number, [key: string]:unknown }` 应该是正确的解决方案 [playground link](https://www.typescriptlang.org/play?ssl=1&amp;ssc=1&amp;pln=6&amp;pc=1# code/JYOwLgpgTgZghgYwgAgGIHt3IN4ChkHIyYBcyIArgLYBG0+hA2gNYQCeZAzmFKAOYBdMhRDMQ6AO4hcAX1y4E6ENyKk0mZAF4cq9GQCMyOYuVhkNOFDIAlCIqgATADzdeIPgBpkIsZJAA+LV0gA) 因为它的意思是:“具有`number` 类型的属性 `foo` 可以用作字典” (2认同)