我刚刚注意到接口无法分配给
Record<string, unknown>
(playground链接):
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 链接):
const foo = { foo: 1 }
const bar: Record<string, unknown> = foo // no error here
问题: 为什么两个示例之间存在差异?对我来说,变量
foo
的简化类型在两个示例中都是相同的......接口 Foo
不应该分配给 Record<string, unknown>
吗?
根据我的理解,
Record<string, unknown>
相当于object
,因此任何接口都应该可以分配给它。另外 @typescript-eslint/ban-types 建议使用 Record<string, unknown>
而不是 object
。
备注: 第一个示例在使用
object
(playground link) 或 Record<string, any>
(playground link) 代替 Record<string, unknown>
时有效。
您遇到过类型中缺少索引签名(仅在接口上,不在类型别名上)#15300
当您将接口更改为类型时,代码将起作用:
type Foo = {
foo: number
}
const foo: Foo = { foo: 1 };
const bar: Record<string, unknown> = foo;
编辑:@Lesiak 上面有正确的答案。我留下这个只是为了相关答案的链接。
诚然,我有点超出了我的深度,但我正在浏览这个答案,我看到:
[A]TypeScript 安全性的很大一部分来自于这样一个事实:[...]只有当它知道它明确地打算作为一个对象时,它才会让您将对象视为字典。
这与我的测试一致。修改您的界面以明确将
Foo.foo
视为索引不会产生错误。 (游乐场链接)
interface Foo {
[foo: string]: number
}
const foo = { foo: 1 }
const bar: Record<string, unknown> = foo
这并不能完全回答您的问题,因为
Record<string, any>
适用于您的显式界面,但也许知识渊博的人可以从这里获取它。
Record<string, unknown>
需要索引签名(参见 GitHub 上的此评论)。
所以这无法编译:
interface X {
a: boolean;
b: number;
}
const x: X = { a: true, b: 1 };
const y: Record<string, unknown> = x; // error
编译成功:
interface X {
[key: string]: unknown;
a: boolean;
b: number;
}
const x: X = { a: true, b: 1 };
const y: Record<string, unknown> = x; // okay
这也可以成功编译,因为推断了类型的索引签名(参见 GitHub 上的此评论):
type X {
a: boolean;
b: number;
}
const x: X = { a: true, b: 1 };
const y: Record<string, unknown> = x; // okay
这也可以成功编译,因为
Record<string, any>
不需要索引签名(参见 GitHub 上的此评论):
interface X {
a: boolean;
b: number;
}
const x: X = { a: true, b: 1 };
const y: Record<string, any> = x; // okay