在打字稿中,以下内容:
interface RecordX extends Record<string, string[]> {
id: string
}
抱怨:
类型'string'的属性'id'不能分配给字符串索引类型'string []'。 ts(2411)
如何将不同类型的属性添加到Record<>
实用程序类型?
更笼统地说,这个问题可能是:如何描述具有异构类型固定属性但动态添加的同类类型属性的对象。
例如给出此对象的示例:
const a = {
id: '123',
count: 123,
set: new Set(),
dynamicItemList: ['X', 'Y']
anotherDynamicallyAddedList: ['Y', 'Z']
} as ExtensibleRecord
对于类型ExtensibleRecord
:
id
,count
和set
的类型和属性键是固定的,在编译时是已知的dynamicItemList
和anotherDynamicallyAddedList
的类型在编译时是已知的,但是它们在对象上的键却不是我尝试了许多我认为可能可行的变体,包括:
type ExtensibleRecord = {
id: string, count: number, set: Set
} & Record<string, string[]>
type ExtensibleRecord = {
id: string, count: number, set: Set
} & Omit<Record<string, string[]>, 'id'|'count'|'set'>
interface ExtensibleRecord = {
id: string,
count: number,
set: Set,
[k: string]: string[]
}
但每个似乎都会导致错误。
这感觉很普通,很明显,但是我找不到示例或参考。
答案 0 :(得分:2)
无法通过官方docs来实现您想要的目标:
虽然字符串索引签名是描述 “字典”模式,它们还强制所有属性匹配 他们的返回类型。这是因为字符串索引声明 obj.property也可以作为obj [“ property”]使用。在下面的 例如,名称的类型与字符串索引的类型不匹配,并且 类型检查器给出错误:
interface NumberDictionary { [index: string]: number; length: number; // ok, length is a number name: string; // error, the type of 'name' is not a subtype of the indexer }
但是,根据此site,在以下情况下可以从索引签名中排除某些属性:当您要为声明的变量建模时。我正在从site复制整个部分。
有时您需要将属性组合到索引签名中。 不建议这样做,您应该使用嵌套索引签名 上面提到的模式。但是,如果您要建模现有的 您可以使用JavaScript使用交叉点类型来解决它。的 以下显示的是您将遇到的错误的示例 使用路口:
type FieldState = { value: string } type FormState = { isValid: boolean // Error: Does not conform to the index signature [fieldName: string]: FieldState }
以下是使用交叉点类型的解决方法:
type FieldState = { value: string } type FormState = { isValid: boolean } & { [fieldName: string]: FieldState }
请注意,即使您可以声明它来建模现有的JavaScript, 您不能使用TypeScript创建这样的对象:
type FieldState = { value: string } type FormState = { isValid: boolean } & { [fieldName: string]: FieldState } // Use it for some JavaScript object you are gettting from somewhere declare const foo:FormState; const isValidBool = foo.isValid; const somethingFieldState = foo['something']; // Using it to create a TypeScript object will not work const bar: FormState = { // Error `isValid` not assignable to `FieldState isValid: false }
最后,作为一种解决方法,如果合适的话,您可以创建一个嵌套接口(检查site的“设计模式:嵌套索引签名”部分,其中动态字段位于该接口的属性中)例如。
interface RecordX {
id: string,
count: number,
set: Set<number>,
dynamicFields: {
[k: string]: string[]
}
}
答案 1 :(得分:1)
如果您没有将其用于class
,则可以使用type
对其进行描述:
type RecordWithID = Record<string, string[]> & {
id: string
}
let x: RecordWithID = {} as any
x.id = 'abc'
x.abc = ['some-string']
答案 2 :(得分:1)
佩德罗的回答是100%正确的,这是我旅程的开始,谢谢!
我想找到最简单的解决方法,并找到一种解决方案:Object.assign()
。似乎除了手动构造这些对象的类型错误之外,甚至在使用扩展运算符时也可能除外,这可能是带有本机JavaScript功能的TypeScript互操作。
在工厂函数中包装Object.assign()
与并行类型结合起来对我来说很优雅,也许对其他人很有用。
// FACTORY WITH TYPE
// Factory uses Object.assign(), which does not error, and returns intersection type
function RecordX(
fixedProps: { id: string },
dynamicProps?: Record<string, string[]>
) {
return Object.assign({}, dynamicProps, fixedProps);
}
// Type name conveniently overlaps with factory and resolves to:
//
// type RecordX = Record<string, string[]> & {
// id: string;
// }
type RecordX = ReturnType<typeof RecordX>;
// USAGE
// Standard use
const model: RecordX = RecordX({ id: 'id-1' }, {
foo: ['a'],
});
// Correct type: string
const id = model.id;
// Correct type: string[]
const otherProp = model.otherProp;
// Appropriately errors with "string is not assignable to string[]"
const model2: RecordX = RecordX({ id: 'id-2' }, {
bar: 'b'
});