Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 如何声明具有任意字符串键的类型化对象-缺少索引签名?_Typescript_Typescript Typings - Fatal编程技术网

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()
或类似函数的变通方法都有可能允许使用编译器不知道(或不记得)的错误属性的意外参数。但让我们看看这些变通方法:

一种解决方法是使用for
ISupplier
而不是接口。显然,这里的折衷是,扩展类型别名并不容易(可以用交叉点获得相同的效果),因此它是“足够安全的”,将其视为隐式索引签名:

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
,它非常干净。