Невозможно распространить дополнительные значения на экземпляр расширенного интерфейса - PullRequest
0 голосов
/ 28 ноября 2018

Я пытаюсь определить инжектор данных (в контексте: props опция graphql HOC клиента Apollo), и я обнаружил, что моя текущая реализация неправильно позволяет некоторым ошибкам игнорировать TypeScript.

Я пробовал много вариантов, представленных ниже, но я не могу найти правильный способ распространения дополнительного ввода вместе с введенными данными, и я все еще застрял с моей текущей небезопасной реализацией (версия 5 в коде ниже, используя типутверждение как any для расширения дополнительного ввода).

Я предполагаю, что что-то упускаю из-за расширения интерфейса и правильного утверждения типа оператора распространения, но я не могу понять, что.

Это с TypeScript 3.0 / 3.1 в строгом режиме.

// data that we want to inject
// 'values' may not be null or undefined
interface InjectedData {
  values: string[];
}

// generic type of a query's result
interface QueryResult<T> {
  // actual data provided by the query
  // the injected values will be extracted from the payload
  data?: {
    error?: string;
    loading: boolean;
    payload?: [{ id: string; name: string }];
  };
  // rest of input data that should be propagated as-is
  inputData: T;
}


// inject 'values' from a query result and propagate any extra input
// the extra input is explicitly forbidden to contain a 'values' field
type Omit<T, K> = Pick<T, Exclude<keyof T, keyof K>>;
const injectValues = <P extends InjectedData>(
  { data, inputData }: QueryResult<Omit<P, InjectedData>>
): P => {
  // 'values' is badly written and possibly undefined. TS detects it, which is good
  // error TS2322: Type 'string[] | undefined' is not assignable to type 'P["values"]'.
  // inputData are not propagated on yet
  const v1: P = {
    values: data && data.payload && data.payload.map(i => i.name)
  };

  // 'values' is fixed, but somehow cannot be assigned to P
  // does the extension of OutData require actual extra fields?
  // error TS2322: Type '{ values: string[]; }' is not assignable to type 'P'.
  // inputData are not propagated on yet
  const v2: P = {
    values: (data && data.payload && data.payload.map(i => i.name)) || []
  };

  // attempt to propagate inputData, but cannot be spread as-is from
  // the Pick type
  // error TS2698: Spread types may only be created from object types
  const v3: P = {
    values: (data && data.payload && data.payload.map(i => i.name)) || [],
    ...inputData
  };

  // type-asserted inputData as object, but then assignment fails again
  // error TS2322: Type '{ values: string[]; }' is not assignable to type 'P'.
  const v4: P = {
    values: (data && data.payload && data.payload.map(i => i.name)) || [],
    ...(inputData as object)
  };

  // type-asserted inputData as any and it compiles...
  const v5_1: P = {
    values: (data && data.payload && data.payload.map(i => i.name)) || [],
    ...(inputData as any)
  };
  // ... but then values is allowed to be undefined again, so possible
  // mistakes might not be caught
  const v5_2: P = {
    values: data && data.payload && data.payload.map(i => i.name), // undefined if data is undefined!
    ...(inputData as any)
  };

  // without inputData, the inferred type of the RHS is {values: string[]}
  const check1 = {
    values: (data && data.payload && data.payload.map(i => i.name)) || []
  };
  // with inputData type-asserted as object, the inferred type of the RHS is {values: string[]}
  const check2 = {
    values: (data && data.payload && data.payload.map(i => i.name)) || [],
    ...(inputData as object)
  };
  // with inputData type-asserted as any, the inferred type of the RHS is any
  const check3 = {
    values: (data && data.payload && data.payload.map(i => i.name)) || [],
    ...(inputData as any)
  };

  // how to achieve both correct checking of 'values' and spreading of inputData?

  return { values: [""] };
};
...