Напишите функцию, которая использует типы поиска на дженериках - PullRequest
2 голосов
/ 31 января 2020

Я хочу написать функцию, которая выполняет три вещи:

  • работает с универсальным c типом T
  • , который принимает ключ: K of T, где T [K] должно быть логическим
  • присваивает значение T [K]

Я следую этому руководству , которое намекает на такую ​​возможность:

type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

interface Part {
    id: number;
    name: string;
    subparts: Part[];
    updatePart(newName: string): void;
}

type T40 = FunctionPropertyNames<Part>;  // "updatePart"
type T41 = NonFunctionPropertyNames<Part>;  // "id" | "name" | "subparts"
type T42 = FunctionProperties<Part>;  // { updatePart(newName: string): void }
type T43 = NonFunctionProperties<Part>;  // { id: number, name: string, subparts: Part[] }

Однако, похоже, я смогу достичь sh своей цели, только если моя функция работает с конкретными типами. Чтобы вычислить логические имена свойств T, я изменил первую строку из примера следующим образом:

type BoolPropNames<T> = { [K in keyof T]: T[K] extends boolean ? K : never }[keyof T];

Это работает, если я использую конкретный тип:

class Thing {
  isGreat: boolean;
}

function assignToThing(thing: Thing, key: BoolPropNames<Thing>) {
  thing[key] = false;
}

Но если я пытаюсь работать с универсальным c, это не так:

function assign<T>(thing: T, key: BoolPropNames<T>) {
  thing[key] = false;
}

Когда я пытаюсь это сделать, TypeScript выдает мне следующую ошибку:

(parameter) key: { [K in keyof T]: T[K] extends boolean ? K : never; }[keyof T]
Type 'false' is not assignable to type 'T[{ [K in keyof T]: T[K] extends boolean ? K : never; }[keyof T]]'.ts(2322)

Почему Разве TypeScript не позволяет мне работать с дженериками таким образом, и что я могу сделать, чтобы это исправить?

1 Ответ

2 голосов
/ 31 января 2020

TS не может вычислить результат BoolPropNames<T>, так как он вычисляется, когда задано T, иначе, чем в предыдущих примерах, где у вас не было переменной типа, но был какой-то определенный тип c. Например, BoolPropNames<Thing> вычисляется в isGreat, а thing[key] известно как boolean.

BoolPropNames<T> не может быть вычислено раньше, поэтому он не знает, что результатом будет ключ, который дает нам boolean. И если он не знает, что think[key] является логическим значением, вы не можете присвоить ему логическое значение.

Чтобы это исправить, мы можем, например, применить аргумент, который будет рассматриваться как тип T[K]. Тогда TS не нужно вычислять тип, поскольку он всегда будет значением типа для данного ключа. Рассмотрим следующий код:

function assign<T, K extends BoolPropNames<T>>(thing: T, key: K, value: T[K]) {
  thing[key] = value;
}
assign({ a: true }, 'a', false);

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

// function which creates assign function
const createAssign = <T, K extends BoolPropNames<T> = BoolPropNames<T>>
(value: T[K]) => (thing: T, key: K) => {
  thing[key] = value;
}
// below assign function is created for type `Example`
type Example = { a: boolean };
const assign = createAssign<Example>(false); 
assign({a: true}, 'a');

При частичном применении мы создали функцию assign, которая работает так же, как и ваша оригинальная, поэтому она присваивает false только полям boolean. Таким способом вы можете создавать все такие функции.

Конечно, недостатком является то, что нам нужно генерировать такие для каждого типа, устанавливая переменную типа generi c, так что это не так полиморфно c как решение с другим порядком аргументов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...