Не могу понять, почему мой пользовательский тип `defaultProps` плохо работает с `Pick`?

#typescript #typescript-typings

#typescript #typescript-типизации

Вопрос:

Я хочу определить DefaultProps тип, который может находить все Undefinable свойства из данного Props типа компонента react, чтобы помочь мне определять defaultProps безопасным для типов способом:

 export type UndefinableKeys<T> = Exclude<{
  [key in keyof T]: undefined extends T[key] ? key : never
} [keyof T], undefined>;

import {UndefinableKeys} from "./UndefinableKeys";

export type DefaultProps<T> = {
  [key in UndefinableKeys<T>]: Exclude<T[key], undefined>
}
  

Затем я определил вспомогательную функцию:

 type OptionalPropsBuilder<P extends object> = {
  withAll: (defaultProps: DefaultProps<P>) => DefaultProps<P>;
  withSome: <DP extends Partial<DefaultProps<P>>>(defaultProps: DP) => Pick<DefaultProps<P>, keyof DP>;
};

export default function undefinableProps<P extends object>(): OptionalPropsBuilder<P> {
  return {
    withAll: (defaultProps) => defaultProps,
    withSome: (defaultProps) => defaultProps,
  };
}
  

Его использование будет похоже:

 type Props = {
  someProp1?: number,
  someProp2?: number
}

const allDefaultProps = undefinableProps<Props>().withAll({
  someProp1: 111,
  someProp2: 222
})

const someDefaultProps = undefinableProps<Props>().withAll({
  someProp1: 111,
  // someProp2: 222
})
  

Но я обнаружил, что в методе есть ошибки компиляции withSome , и не могу понять, почему после нескольких часов отладки.

  1. Почему Pick<DefaultProps<P>, keyof DP> ошибка компиляции в keyof DP части
  2. Я попытался изменить его на: Pick<DefaultProps<P>, Extract<keyof DefaultProps<P>, keyof DP>> , но у него все та же ошибка компиляции

Комментарии:

1. Что, если вы измените Pick<DefaultProps<P>, keyof DP> на Required<DP> ? Я думаю, это должно работать так, как вы хотите.

Ответ №1:

Из-за всего сопоставления typescript потерял знания, которые DP являются подмножеством P , и поэтому ключи DP также должны быть ключами P .

Хотя в этом нет необходимости, потому что Pick<DefaultProps<P>, Extract<keyof DefaultProps<P>, keyof DP>> что на самом деле? Это то же самое, что сказать DP . Просто верните DP .

Очевидно, ваш код не совсем закончен, поэтому я еще немного поиграл с ним.

Мы определяем a PropFiller для объекта P как функцию, которая принимает набор допустимых props: P и возвращает набор реквизитов, который выполняет P , а также добавляет некоторое подмножество необязательных свойств по мере необходимости.

 type PropFiller<P extends object, DP extends Partial<DefaultProps<P>>> = (props: P) => P amp; DP;
  

Мы реализуем это с помощью функции более высокого порядка, которая принимает набор defaultProps: DP и возвращает PropFiller<P, DP> функцию.

 const createPropFiller = <P extends object, DP extends Partial<DefaultProps<P>>>(defaultProps: DP): PropFiller<P, DP> => 
  (props: P) => ({
    ...defaultProps,
    ...props
  });
  

Наш OptionalPropsBuilder тип включает в себя два случая createPropFiller . withAll требуется, чтобы PropFiller DefaultProps<P> addsвсе время withSome может принимать любое подмножество.

 type OptionalPropsBuilder<P extends object> = {
  withAll: (defaultProps: DefaultProps<P>) => PropFiller<P, DefaultProps<P>>;
  withSome: <DP extends Partial<DefaultProps<P>>>(defaultProps: DP) => PropFiller<P, DP>;
};

function undefinableProps<P extends object>(): OptionalPropsBuilder<P> {
  return {
    withAll: createPropFiller,
    withSome: createPropFiller,
  };
}
  

Ссылка на игровую площадку Typescript