TypeScript如何才能定义一个约束指定对象键不同的数组?
这里是demo that indicates the requirements。
type Magician = {
distinctKey: string;
lastName: string;
};
// How, if at all, can we make line 19 be a compiler error?
type ObjectArrayWithDistinctKey<TItem, TKey> = TItem[];
const example: ObjectArrayWithDistinctKey<Magician, 'distinctKey'> = [
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
{
distinctKey: 'vernon1894',
lastName: 'Vernon'
},
{
distinctKey: 'ammar1956', // error!
lastName: 'Ammar'
}
];
我很高兴这是Map(而不是数组)之类的工作。
答案 0 :(得分:1)
不,这是数组中无法实现的行为,原因是数组只能是单一类型“ A”,A不能将自己与其他A进行比较。
这是可以通过元组或可变长度元组实现的行为。
编辑:有很多方法可以用元组实现这一点,我会说,没有最好的方法真正取决于您的整体情况,这就是为什么我没有添加解决方案,但是这至少是一种方法将MagicianA | MagicianB
组合成元组[MagicianA | MagicianB] | [MagicianB, MagicianA]
的排列,这使顺序无关紧要,而是强制使用不同的键和长度。
interface Magician<DistinctKey extends "ammar1956" | "vernon1894"> {
distinctKey: DistinctKey;
lastName: string;
};
type AmmarMagician = Magician<"ammar1956">;
type VernonMagician = Magician<"vernon1894">;
/* Easiest quality of life solution */
type Overwrite<T, S extends any> = { [P in keyof T]: S[P] };
type TupleUnshift<T extends any[], X> = T extends any ? ((x: X, ...t: T) => void) extends (...t: infer R) => void ? R : never : never;
type TuplePush<T extends any[], X> = T extends any ? Overwrite<TupleUnshift<T, any>, T & { [x: string]: X }> : never;
type UnionToTuple<U> = UnionToTupleRecursively<[], U>;
type UnionToTupleRecursively<T extends any[], U> = {
1: T;
0: UnionToTupleRecursively_<T, U, U>;
}[[U] extends [never] ? 1 : 0]
type UnionToTupleRecursively_<T extends any[], U, S> =
S extends any ? UnionToTupleRecursively<TupleUnshift<T, S> | TuplePush<T, S>, Exclude<U, S>> : never;
/* End Easiest qulaity of life solution */
const example: UnionToTuple<AmmarMagician | VernonMagician> = [
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
{
distinctKey: 'vernon1894',
lastName: 'Vernon'
},
]; // works
const example2: UnionToTuple<AmmarMagician | VernonMagician> = [
{
distinctKey: 'vernon1894',
lastName: 'Vernon'
},
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
]; // works swapped
const example3: UnionToTuple<AmmarMagician | VernonMagician> = [
{
distinctKey: 'vernon1894',
lastName: 'Vernon'
},
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
]; // fails duplicates.
或者您也可以对可变长度执行以下操作。...
type ValidKeys = "ammar1956" | "vernon1894" | "UnknownMagician"
interface Magician<DistinctKey extends ValidKeys> {
distinctKey: DistinctKey;
lastName: string;
};
type AmmarMagician = Magician<"ammar1956">;
type VernonMagician = Magician<"vernon1894">;
type UnknownMagician = Magician<"UnknownMagician">;
/* Easiest quality of life solution */
type MagiciansArray = [AmmarMagician, VernonMagician, ...UnknownMagician[]];
/* End Easiest qulaity of life solution */
const example: MagiciansArray = [
{
distinctKey: 'ammar1956',
lastName: 'Ammar'
},
{
distinctKey: 'vernon1894',
lastName: 'Vernon'
},
{
distinctKey: "UnknownMagician",
lastName: "hello"
}
]; // works