Typescript 如何声明具有任意字符串键的类型化对象-缺少索引签名?
我需要声明一个对象,该对象具有任意字符串键和Typescript 如何声明具有任意字符串键的类型化对象-缺少索引签名?,typescript,typescript-typings,Typescript,Typescript Typings,我需要声明一个对象,该对象具有任意字符串键和number或string类型的值,即类似于中的值 所以我写了 interface AnyObjectWithNumberAndStringValues { [s: string]: number | string; } 和一个测试函数 function f(x: AnyObjectWithNumberAndStringValues) { console.log(x); } 这是一种工作。我可以打电话 f({ id: 123, name:
number
或string
类型的值,即类似于中的值
所以我写了
interface AnyObjectWithNumberAndStringValues {
[s: string]: number | string;
}
和一个测试函数
function f(x: AnyObjectWithNumberAndStringValues) {
console.log(x);
}
这是一种工作。我可以打电话
f({ id: 123, name: "noname" })
但是现在,我有了一个接口和一个对象,比如
interface ISupplier {
id: number;
name: string;
}
const o: ISupplier = { id: 123, name: "noname" };
调用f(o)
会产生以下错误:
“ISupplier”类型的参数不能分配给“AnyObjectWithNumberAndStringValues”类型的参数。
类型“ISupplier”.ts(2345)中缺少索引签名
- 有人能解释一下原因吗
- 我如何解决这个问题
f
接受任何对象,只要它的值只是数字和字符串
请参见下面的完整示例。请注意,它似乎是不确定的:错误可能会出现,也可能不会出现 弄清楚哪些类型可以分配给某个对象,哪些类型可以分配给某个对象,哪些类型可以分配给某个对象,肯定会让人感到困惑。 原因是什么
f({ id: 123, name: "noname" }); // okay
const o: ISupplier = { id: 123, name: "noname" };
f(o); // error
之所以有效,是因为参数是一个对象文字,它被赋予了一个。发生这种情况时,编译器知道该参数是一个对象文本,因此它没有任何未知属性。因此,可以安全地将其视为可分配给具有NumberAndStringValues的任何对象
原因是什么
f({ id: 123, name: "noname" }); // okay
const o: ISupplier = { id: 123, name: "noname" };
f(o); // error
不起作用是因为o
被赋予了ISupplier
的类型注释。当调用f(o)
时,编译器已经忘记了o
中的特定属性。编译器知道其中可能有未知的非字符串
和非数字
属性
考虑以下代码:
interface ISupriser extends ISupplier {
surprise: boolean;
}
const s: ISupriser = { id: 123, name: "noname", surprise: true };
const uhOh: ISupplier = s;
f(uhOh); // error makes sense now
ISurpriser
包含一个boolean
属性,因此您不想用它调用f()
。但是每个ISupriser
也是一个issupplier
。变量uhOh
对ISupplier
的有效性与您的o
一样。编译器对o
和uhOh
的所有记忆都是ISupplier
的实例。这就是为什么f(o)
失败的原因。。。这是为了防止您意外地呼叫f(uhOh)
请注意,任何允许对非对象文本的对象调用
f()
或类似函数的变通方法都有可能允许使用编译器不知道(或不记得)的错误属性的意外参数。但让我们看看这些变通方法:
一种解决方法是使用forISupplier
而不是接口。显然,这里的折衷是,扩展类型别名并不容易(可以用交叉点获得相同的效果),因此它是“足够安全的”,将其视为隐式索引签名:
type TSupplier = {
id: number;
name: string;
};
const p: TSupplier = { id: 123, name: "noname" };
f(p); // okay
添加不需要的属性可能不像添加接口那样“容易”,但仍然很容易:
const ohNo: TSupplier = uhOh; // okay
f(ohNo); // no error, but there probably should be one!
另一种解决方法,尤其是当您无法更改
ISupplier
时,是在函数中使用受约束的泛型类型,而不是索引签名,如下所示:
function g<T extends Record<keyof T, number | string>>(x: T) {
console.log(x);
}
同样,如果编译器不知道某个属性,则无法检查该属性,因此这些属性也没有错误:
g(uhOh); // no error, but there probably should be one
g(ohNo); // no error, but there probably should be one
不管怎样,希望这对你有帮助。祝你好运 GitHub中的相关问题:太棒了。我被“ISupplier类型中缺少索引签名”误导了,这听起来像是缺少了什么,但缺少的是任何
ISupplier
都没有错误键入的值的约束。使用您的ISurpriser
,它非常干净。