我需要声明一个具有任意字符串键和类型为number
或string
的值的对象,即类似this question中的值。
所以我写了
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
的值只是数字和字符串,我如何声明它可以接受任何对象?
有关完整示例,请参见此sandbox。请注意,这似乎是不确定的:错误可能会或可能不会出现!
答案 0 :(得分:1)
弄清楚可以分配给indexable type和来自implicit index signature的类型是绝对令人困惑的。
f({ id: 123, name: "noname" }); // okay
之所以有用,是因为该参数是对象文字,它被赋予noted。发生这种情况时,编译器知道该参数是对象文字,因此它没有任何未知的属性。因此,将其视为可分配给AnyObjectWithNumberAndStringValues
的对象是安全的。
原因
const o: ISupplier = { id: 123, name: "noname" };
f(o); // error
不起作用是因为o
被赋予了ISupplier
的类型注释。在调用f(o)
时,编译器已经忘记了o
内部的特定属性。对于所有编译器知道的信息,其中可能存在未知的非string
和非number
属性。
考虑以下代码:
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
也是一个ISupplier
。变量uhOh
与ISupplier
一样对o
有效。并且所有编译器都记得o
和uhOh
都是它们是ISupplier
的实例。这就是f(o)
失败的原因...是为了防止您意外呼叫f(uhOh)
。
请注意,您所做的任何变通办法都可以在不是对象文字的对象上调用f()
或类似的函数,这有可能允许意外的参数,而编译器不知道这些属性具有错误的属性。 (或不记得了)。但是还是让我们看看这些变通方法:
type alias的一种解决方法是为ISupplier
使用Link to code而不是接口。显然,这里的权衡是扩展类型别名不是那么容易(使用交集可以得到相同的效果),因此将其视为具有隐式索引签名是“足够安全的”:
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()
函数基本上只接受其已知属性可分配给number | string
的参数:
g({ id: 123, name: "noname" }); // okay
g(o); // okay
g(s); // error, "surprise" is incompatible
同样,如果编译器不知道某个属性,则无法对其进行检查,因此这些也没有错误:
g(uhOh); // no error, but there probably should be one
g(ohNo); // no error, but there probably should be one
无论如何,希望对您有所帮助。祝你好运!
{{3}}