使用不同的对象键定义对象数组

时间:2019-05-31 01:27:44

标签: typescript types

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(而不是数组)之类的工作。

1 个答案:

答案 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