Я пытаюсь определить инжектор данных (в контексте: 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: [""] };
};