是否可以扩展内置的Record(或{[key:string]:string}接口),在其中您还可以定义一些固定键及其类型?
让我们说这个:
const otherValues = {
some: 'some',
other: 'other',
values: 'values',
}
const composedDictionary = {
id: 1,
...otherValues
}
我想为compositionDictionary定义一个接口,其中id
的类型为number
(仅数字),其他所有类型的类型为string
。
我已经尝试过了:
interface ExtendedRecord extends Record<string, string> {
id: number;
}
还有:
interface MyDictionary {
[key: string]: string;
id: number;
}
均失败:
Property 'id' of type 'number' is not assignable to string index type 'string'
有什么想法吗?
答案 0 :(得分:1)
理想情况下,索引签名应反映任何可能的索引操作结果。如果使用不可检查的字符串键访问composedDictionary
,则如果number
实际上是string
(例如:'id'
),则结果可能是composedDictionary['id' as string]
,键入内容将显示是string
,但在运行时却是number
)。这就是类型系统在此问题上与您抗争的原因,这是不一致的类型。
您可以定义索引以使所有属性保持一致:
interface MyDictionary {
[key: string]: string | number;
id: number;
}
打字稿检查索引和属性的一致性存在漏洞。环形孔是交叉点类型:
type MyDictionary = Record<string, string> & {
id: number
}
const composedDictionary: MyDictionary = Object.assign({
id: 1,
}, {
...otherValues
});
编译器仍然会在分配上与您抗争,在类型系统中创建此类不一致对象的唯一方法是使用Object.assign
答案 1 :(得分:1)
正如另一个答案所说,TypeScript不支持其中某些属性是索引签名的 exceptions 的类型,因此无法将您的MyDictionary
表示为一致的具体类型。不一致的相交解决方案({[k: string]: string]} & {id: number}
)可以用于属性读取,但是很难用于属性写入。
有一个旧的suggestion允许“其余”索引签名,在这里您可以说索引签名应该表示除指定属性以外的所有属性 。
还有一些更新(但可能被搁置)的实现negated types和arbitrary key types for index signatures的增强功能,它们将allow you to represent such exception/default index signature properties像{ id: number; [k: string & not "id"]: string }
一样。但这还不能编译(TS3.5),并且可能永远也不会编译,因此这只是现在的梦想。
因此您不能将MyDictionary
表示为具体类型。但是,您可以 将其表示为generic constraint。突然使用它要求您所有以前的具体函数必须成为通用函数,并且您先前的具体值必须成为通用函数的输出。因此,它可能是太多的机械,而不是值得的。尽管如此,让我们看看如何做到这一点:
type MyDictionary<T extends object> = { id: number } & {
[K in keyof T]: K extends "id" ? number : string
};
在这种情况下,MyDictionary<T>
采用候选类型T
,并将其转换为与所需MyDictionary
类型相匹配的版本。然后,我们使用以下帮助函数来检查是否匹配:
const asMyDictionary = <T extends MyDictionary<T>>(dict: T) => dict;
注意自引用约束T extends MyDictionary<T>
。因此,这是您的用例及其工作方式:
const otherValues = {
some: "some",
other: "other",
values: "values"
};
const composedDictionary = asMyDictionary({
id: 1,
...otherValues
}); // okay
该编译没有错误,因为asMyDictionary()
的参数是有效的MyDictionary<T>
。现在让我们看看一些失败的地方:
const invalidDictionary = asMyDictionary({
id: 1,
oops: 2 // error! number is not a string
})
const invalidDictionary2 = asMyDictionary({
some: "some" // error! property id is missing
})
const invalidDictionary3 = asMyDictionary({
id: "oops", // error! string is not a number
some: "some"
})
编译器会捕获所有这些错误,并告诉您问题出在哪里。
因此,从TS3.5开始,这是我所能获得的最接近的功能。好的,希望能有所帮助;祝你好运!