我有一个数据结构,其中一个键允许动态值集。我知道这些值的潜在类型,但我无法在Typescript中表达这一点。
interface KnownDynamicType {
propA: boolean;
}
interface OtherKnownDynamicType {
propB: number;
}
// I want to allow dynamic to accept either KnownDynamicType, OtherKnownDynamicType or a string as a value
interface DataModel {
name: string;
dynamic: {[key: string]: KnownDynamicType | OtherKnownDynamicType | string};
}
const data: DataModel = { // Set up some values
name: 'My Data Model',
dynamic: {
someKnownType: {
propA: true
},
someOtherKnownType: {
propB: 1
},
someField1: 'foo',
someField2: 'bar'
}
}
data.dynamic.foo = 'bar'; // Fine
data.dynamic.someObject = { // <--- This should be invalid
propA: false,
propB: ''
}
Typescript似乎将data.dynamic.someObject
视为KnownDynamicType
,但出乎意料地允许我将OtherKnownDynamicType
的属性分配给它。
我尽最大努力避免复杂的打字,但是当情况出现时,我宁愿避免放弃,只需设置dynamic: any
我的问题是,如何在打字稿中正确表达上述内容?
答案 0 :(得分:3)
联盟确实是包容性的,因此A | B
包含任何有效的A
,以及任何有效B
的内容,包括任何有效的A & B
(A | B) & !(A & B)
1}}。 TypeScript没有negated types,因此您无法说出像A
这样的内容,它会明确排除任何与B
和A
匹配的内容。哦,好吧。
TypeScript也缺少exact types,因此向有效的A
添加属性仍会产生有效的Exact<A> | Exact<B>
。因此,您无法说出A
,这也可以排除与B
和A
匹配的任何内容,以及除明确之外的任何其他属性的内容声明了B
或type ProhibitKeys<K extends keyof any> = { [P in K]?: never }
type Xor<T, U> = (T & ProhibitKeys<Exclude<keyof U, keyof T>>) |
(U & ProhibitKeys<Exclude<keyof T, keyof U>>);
的属性。哦,好吧。
TypeScript 确实拥有excess property checking,它通过禁止&#34; fresh&#34;提供额外属性来提供否定或确切类型的一些好处。对象文字。不幸的是,(从TypeScript v2.8开始)有一个issue,其中对联合类型的多余属性检查并不像大多数人想要的那样严格,正如您所发现的那样。看起来这个问题被认为是一个错误,应该在 TypeScript v3.0 (编辑:)中解决未来,但这并不能解决你今天的问题。哦,好吧。
所以,也许我们可以使用我们的类型来获得类似于你想要的行为:
ProhibitKeys<K>
让我们检查一下。 K
返回一个类型,其中包含所有可选属性,这些属性的键位于never
且其值为ProhibitKeys<"foo" | "bar">
。因此{foo?: never, bar?: never}
相当于never
。由于ProhibitKeys<K>
是impossible type,因此真实对象与K
匹配的唯一方法是不要使用其键位于Xor<T,U>
的任何属性。因此这个名字。
现在让我们看一下A
。类型Exclude<keyof A, keyof B>
是B
中所有已声明的键的并集,这些键在Xor<T,U>
中也不会显示为键。 T & ProhibitKeys<Exclude<keyof U, keyof T>>
是两种类型的联合。第一个是T
,这意味着它是有效的U
,没有来自T
的属性(除非这些属性也在U & ProhibitKeys<Exclude<keyof T, keyof U>>
中)。第二个是U
,这意味着有效的T
没有来自DataModel
的属性。这与我在TypeScript中表达你想要的独占联盟的想法非常接近。让我们使用它,看看它是否有效。
首先,更改interface DataModel {
name: string;
dynamic: { [key: string]: Xor<KnownDynamicType, OtherKnownDynamicType> | string };
}
:
declare const data: DataModel;
data.dynamic.foo = 'bar'; // okay
data.dynamic.someObject = {
propA: false,
propB: 0
}; // error! propB is incompatible; number is not undefined.
data.dynamic.someObject = {
propA: false
}; // okay now
data.dynamic.someObject = {
propB: 0
}; // okay now
data.dynamic.someObject = {
propA: false,
propC: 0 // error! unknown property
}
让我们看看会发生什么:
1- <a class="godown" id="<?=$row->id_apoint;?>"><?=$row->nombre;?></a>
2- <button class="bill" >button1</button> <button class="bill" >button2</button>
这一切都按预期工作。希望有所帮助。祝你好运!