Почему новый тип "Pick" разрешает подмножества " K " в "setState ()" React?

я думал, что понял цель нового TS 2.1 Pick тип, но потом увидел как он использовался в определениях типа React и я не понимаю:

declare class Component<S> {
    setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
    state: Readonly<S>;
}

что позволяет вам сделать это:

interface PersonProps {
  name: string;
  age: number;
}

class Person extends Component<{}, PersonProps> {
  test() {
    this.setState({ age: 123 });
  }
}

мое замешательство вот в чем keyof S is { name, age } но я называю setState() С age -- почему он не жалуется на недостающие name?

моя первая мысль такова, потому что Pick это тип индекса, он просто не требует, чтобы все ключи существовали. Обретать смысл. Но если я попытаюсь назначить тип напрямую:

const ageState: Pick<PersonProps, keyof PersonProps> = { age: 123 };

это тут пожаловаться на пропавших

1 ответов


короткий ответ: если вы действительно хотите явный тип, вы можете использовать Pick<PersonProps, "age">, но вместо этого проще использовать неявное приведение типов.

ответ:

ключевым моментом является то, что K является переменной универсального типа, которая выходит keyof T.

тип keyof PersonProps равно объединению строк "name" | "age". Тип "age" можно сказать, чтобы расширить тип "name" | "age".

вспомните определение Pick - это:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}

что означает для каждого K, объект, описываемый этим типом, должен иметь свойство P того же типа, что и свойство K на T. Ваш пример кода игровой площадки:

const person: Pick<PersonProps, keyof PersonProps> = { age: 123 };

разворачивая переменные универсального типа, мы получаем:

  • Pick<T, K extends keyof T>,
  • Pick<PersonProps, "name" | "age">,
  • [P in "name" | "age"]: PersonProps[P] и наконец
  • {name: string, age: number}.

это, конечно, несовместимо с { age: 123 }. Если вы вместо этого скажете:

const person: Pick<PersonProps, "age"> = { age: 123 };

тогда, следуя той же логике, типа person будет правильно эквивалентно {age: number}.

конечно, TypeScript вычисляет все эти типы для вас в любом случае-вот как вы получили ошибку. Поскольку TypeScript уже знает типы {age: number} и Pick<PersonProps, "age"> совместимы, вы также можете сохранить тип impicit:

const person = { age: 123 };