Man*_*anu 28 casting typescript
我希望MyInterface.dic像字典一样name: value,我将其定义如下:
interface MyInterface {
dic: { [name: string]: number }
}
Run Code Online (Sandbox Code Playgroud)
现在我创建一个等待我的类型的函数:
function foo(a: MyInterface) {
...
}
Run Code Online (Sandbox Code Playgroud)
并输入:
let o = {
dic: {
'a': 3,
'b': 5
}
}
Run Code Online (Sandbox Code Playgroud)
我期待foo(o)是正确的,但编译器正在下降:
foo(o) // Typescript error: Index signature is missing in type { 'a': number, 'b': number }
Run Code Online (Sandbox Code Playgroud)
我知道有一个可能的演员:let o: MyInterface = { ... }哪个可以做,但问题是,为什么打字稿不能识别我的类型?
额外:如果o内联声明,则工作正常:
foo({
dic: {
'a': 3,
'b': 5
}
})
Run Code Online (Sandbox Code Playgroud)
Jöc*_*ker 40
就我而言,只需使用type而不是interface.
Tim*_*rry 32
问题是当推断出类型时,那么类型o是:
{ dic: { a: number, b: number } }
Run Code Online (Sandbox Code Playgroud)
那不一样{ dic: { [name: string]: number } }.至关重要的是,使用顶级签名,您不能做类似的事情o.dic['x'] = 1.有了第二个签名.
它们在运行时是等效的类型(实际上,它们是完全相同的值),但TypeScript安全的很大一部分来自于这些不同的事实,并且它只会让你将对象视为一个字典,如果它知道它明确意图作为一个.这就是阻止你不小心在对象上读写完全不存在的属性的原因.
解决方案是确保TypeScript知道它是作为字典.这意味着:
明确地提供某个类型,告诉它它是一个字典:
let o: MyInterface
断言它是字典内联:
let o = { dic: <{ [name: string]: number }> { 'a': 1, 'b': 2 } }
确保它是TypeScript为您推断的初始类型:
foo({ dic: { 'a': 1, 'b': 2 } })
如果有一种情况是TypeScript认为它只是一个只有两个属性的普通对象,那么你以后会尝试将它用作字典,那就太不开心了.
C. *_*wis 20
TS 要我们定义索引的类型。例如,告诉编译器您可以使用任何字符串索引对象,例如myObj['anyString'],更改:
interface myInterface {
myVal: string;
}
Run Code Online (Sandbox Code Playgroud)
到:
interface myInterface {
[key: string]: string;
myVal: string;
}
Run Code Online (Sandbox Code Playgroud)
您现在可以在任何字符串索引上存储任何字符串值:
x['myVal'] = 'hello world'
x['any other string'] = 'any other string'
Run Code Online (Sandbox Code Playgroud)
Tim*_*aas 20
这是我的两分钱:
type Copy<T> = { [K in keyof T]: T[K] }
genericFunc<SomeType>() // No index signature
genericFunc<Copy<SomeType>>() // No error
Run Code Online (Sandbox Code Playgroud)
Nik*_*nyy 12
对我来说,错误是通过使用类型而不是接口解决的
当函数foo具有类型而不是用于键入参数的接口时,可能会发生此错误
type MyType {
dic: { [name: string]: number }
}
function foo(a: MyType) {}
Run Code Online (Sandbox Code Playgroud)
但是通过像这样的接口输入的值
interface MyInterface {
dic: { [name: string]: number }
}
const o: MyInterface = {
dic: {
'a': 3,
'b': 5
}
}
foo(o) // type error here
Run Code Online (Sandbox Code Playgroud)
我刚用
const o: MyType = {
dic: {
'a': 3,
'b': 5
}
}
foo(o) // it works
Run Code Online (Sandbox Code Playgroud)
Web*_*her 12
这个问题比OP的问题更广泛。
例如,我们定义一个接口和该接口的变量
interface IObj {
prop: string;
}
const obj: IObj = { prop: 'string' };
Run Code Online (Sandbox Code Playgroud)
我们可以分配obj给 typeRecord<string, string>吗?
答案是不。演示
// TS2322: Type 'IObj' is not assignable to type 'Record<string, string>'. Index signature for type 'string' is missing in type 'IObj'.
const record: Record<string, string> = obj;
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种情况?为了描述它,让我们刷新一下我们对“向上转型”和“向下转型”术语的理解,以及 SOLID 原则中“L”字母的含义。
以下示例可以正常运行,因为我们将“更宽”的类型分配给更严格的类型。
const initialObj = {
title: 'title',
value: 42,
};
interface IObj {
title: string;
}
const obj: IObj = initialObj; // No error here
obj.title;
obj.value; // Property 'value' does not exist on type 'IObj'.(2339)
Run Code Online (Sandbox Code Playgroud)
IObj仅需要一个道具,因此分配是正确的。
对于类型也是如此。演示
const initialObj = {
title: 'title',
value: 42,
};
type TObj = {
title: string;
}
const obj: TObj = initialObj; // No error here
obj.title;
obj.value; // Property 'value' does not exist on type 'TObj'.(2339)
Run Code Online (Sandbox Code Playgroud)
由于“向上转换”,最后两个示例可以正常工作而不会出现错误。这意味着我们将值类型转换为“上层”类型,即可以作为祖先的实体类型。换句话说,我们可以将 Dog 分配给 Animal,但不能将 Animal 分配给 Dog(参见 SOLID 原则中“L”字母的含义)。将狗分配给动物是“向上转型”,这是安全操作。
Record<string, string>比只有一个属性的对象要宽得多。它可以具有任何其他属性。
const fn = (record: Record<string, string>) => {
record.value1;
record.value2;
record.value3; // No errors here
}
Run Code Online (Sandbox Code Playgroud)
这就是为什么当您分配接口时IObj会Record<string, string>出现错误。您将其分配给扩展的类型IObj。Record<string, string>类型可以是 的后代IObj。
在其他答案中,提到使用Type可以解决问题。但我认为这是错误的行为,我们应该避免使用它。
type TObj = {
title: string;
}
const obj: TObj = {
title: 'title',
};
const fn = (record: Record<string, string>) => {
record.value1;
record.value2;
// No errors here because according to types any string property is correct
// UPD:
// FYI: TS has a flag `noUncheckedIndexedAccess` which changes this behavior so every prop becomes optional
record.value3;
}
fn(obj); // No error here but it has to be here because of downcasting
Run Code Online (Sandbox Code Playgroud)
聚苯乙烯
这个错误是有效的。您应该编写类似以下选项的内容:
const o = Object.freeze({dic: {'a': 3, 'b': 5}})
const o = {dic: {'a': 3, 'b': 5}} as const
const o: MyInterface = {dic: {'a': 3, 'b': 5}}
Run Code Online (Sandbox Code Playgroud)
为什么?
TypeScript 编译器不能假设o在初始化时间和foo(o)调用时间之间不会发生变化。
也许在你的代码中的某个地方写了类似下面的代码片段:
delete o.dic
Run Code Online (Sandbox Code Playgroud)
这就是内联版本起作用的原因。在这种情况下,没有任何可能的更新。