TypeScript - ограничение для массива - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть следующий (пример-) массив:

[
  'test-string', 
  foo => ({ foo }), 
  'other-string', 
  bar => ({ bar })
]

с (пример-) интерфейсом

interface MyValues { foo: string; bar: string; }

Функция, которая ожидает этот тип массива, должна гарантировать, что объединение результатов всех функций реализует полный интерфейс. Это возможно? В настоящее время у меня есть:

type Fn<Values> = (part: string) => { [key in keyof Values]?: string }
type Item<Values> = string | Fn<Values>;

function extract<Values>(items: Item<Values>[]) {
  const values = {} as Values;
  // (...)
  return values;
}

Однако эта типизация проверяет только то, что все функции возвращают объекты, соответствующие ключам Values, а не то, что все ключи наконец присутствуют.

Я не совсем уверен, возможна ли эта проверка даже с TypeScript, я нашел этот ответ , который также идет в направлении «вычислений» типов, но я не уверен, применима ли это для этого использования -случае.

1 Ответ

0 голосов
/ 04 сентября 2018

Я вполне уверен, что вы не можете написать тип, члены которого являются массивами той формы, которую вы хотите. Если вы позволите TypeScript выводить тип элемента массива как объединение типов его членов, вы можете затем проверить, что тип элемента отображается для полного интерфейса:

interface MyValues { foo: string; bar: string; }

// From jcalz, https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type Fn<Values> = (part: string) => { [key in keyof Values]?: string }
type Item<Values> = string | Fn<Values>;
type CheckComplete<Values, T extends Item<Values>> =
  // note: ReturnType is distributive.
  UnionToIntersection<ReturnType<Exclude<T, string>>> extends
  { [key in keyof Values]: string } ? unknown : never;

function extractFactory<Values>() {
  return function<T extends Item<Values>>(items: T[] & CheckComplete<Values, T>) {
    const values = {} as Values;
    // (...)
    return values;  
  };
}

const extract = extractFactory<MyValues>();

const a = [
  'test-string', 
  foo => ({ foo }), 
  'other-string', 
  bar => ({ bar })
];
extract(a);  // OK

const b = [
  'test-string', 
  foo => ({ foo }), 
  'other-string'
];
extract(b);  // Error

Однако проверку легко обойти с помощью аннотации, которая устанавливает тип элемента в объединение, а не все члены которого фактически отображаются в массиве:

extract<string
  | ((arg: string) => {foo: string})
  | ((arg: string) => {bar: string})>(b);  // Wrong but no error
...