为什么一个接口不能分配给Record<string, unknown>?

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

我刚刚注意到无法将接口分配给 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)

然而,当 Foo 的类型声明被省略时(playground link)也是可能的:

const foo = { foo: 1 }
const bar: Record<string, unknown> = foo // no error here

问题:为什么两个示例之间存在差异?对我来说,变量 foo 的简化类型在两个示例中都是相同的...接口 Foo 不应该分配给 Record<string, unknwon> 吗?!

在我的理解中,Record<string, unknown> 等同于 object,因此任何接口都应该可以分配给它。另外 https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md 建议使用 Record<string, unknown> 而不是 object.

备注:第一个示例在使用 objectplayground link) or Record<string, any> (playground link)而不是 Record<string, unknown> 时有效。

编辑:@Lesiak 在上面有正确答案。我将此留给相关答案的 link。

诚然,我在这里有点不知所云,但我正在浏览 并且我看到:

[A] big part of TypeScript's safety comes from the fact that [...] it'll only let you treat an object as a dictionary if it knows it's explicitly intended as one.

这与我的测试一致。修改您的界面以明确地将 Foo.foo 视为索引不会产生错误。 (playground link)

interface Foo {
    [foo: string]: number
}

const foo = { foo: 1 }
const bar: Record<string, unknown> = foo

这并没有完全回答您的问题,因为 Record<string, any> 使用您的显式界面,但也许更有知识的人可以从这里获取它。

您 运行 跨越了 Index signature is missing in type (only on interfaces, not on type alias) #15300

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

type Foo = {
    foo: number
}

const foo: Foo = { foo: 1 };
const bar: Record<string, unknown> = foo;