Универсальная карри-функция TypeScript с индексным типом - PullRequest
0 голосов
/ 18 декабря 2018

Я создал каррированную функцию TypeScript, которая сначала получает имя свойства в виде строки, а затем объект для получения значения этого свойства.Я использовал тип индекса, чтобы убедиться, что я получаю сообщение об ошибке при попытке получить доступ к несуществующему свойству:

export interface Dict {
  name: string;
  age: number;
}

const prop = <T extends Dict, K extends keyof T>(p: K) => (obj: T): T[K] => obj[p];

prop('name')({name: 'John', age: 45});  // John
prop('name2')({name: 'John', age: 45});  // error...

В последней строке выдается ошибка:

error TS2345: Argument of type '"name2"' is not assignable to parameter of type '"name" | "age"'.

, чтоименно то, что я хочу, так как свойство name2 не существует для объекта, заданного в качестве второго аргумента.

Однако, когда я пытаюсь создать безопасную версию, используя монаду Maybe, выдается похожая ошибка:

const safeProp = <T extends Dict, K extends keyof T>(p: K) => (obj: T): Maybe<{}> => compose2(Maybe.of, prop(p))(obj);

Ошибка связана со вторым аргументом функции compose2: prop(p):

Argument of type 'K' is not assignable to parameter of type '"name" | "age"'.

Что я не понимаю, так как объявил K extends keyof T, что я предположилбыть правильной, поскольку она также работает для функции prop.

Для справки: функция compose2:

const compose2 = <A, B, C>(f: (b: B) => C, g: (a: A) => B): ((a: A) => C) => a => f(g(a));

и соответствующая часть Maybe монады:

class Maybe<A> {
  static of<A>(x: A): Maybe<A> {
    return new Maybe(x);
  }

  ...
}

Как правильно ввести функцию safeProp и почему мне нужно указать тип возвращаемого значения Maybe<{}>, а не Maybe<T[K]>?

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018
const safeProp = <T extends Dict, K extends keyof Dict>(p: K) => (obj: T): Maybe<{}> => compose2(Maybe.of, prop(p))(obj);

Приведенный выше код не выдает ошибку, и я попытаюсь объяснить, почему (и почему ваш код делает).

K extends keyof Dict - это точно name или age (которые являются только допустимыми параметрами для функции prop).И K extends keyof T может быть любой клавишей типа number | string | Symbol.Потому что если T extends Dict, это не значит, что у него не может быть никаких других ключей, таких как «фамилия» и т. Д. Другими словами:

type IsDict = {name: string, age: number, lastname: string} extends Dict ? true : false // IsDict is true

Видите?Ваш код разрешил бы ввод ключа lastname, а Typescript защищает вас от этого.

Тем не менее, в настоящее время машинопись не имеет средства для ограничения параметра типа до точного *Тип 1022 * (есть обходные пути , но, к сожалению, они не подходят для вашего случая).

0 голосов
/ 18 декабря 2018

Здесь проблема , детализирующая, как Typescript неправильно выводит типы при передаче универсальных функций в универсальные функции высшего порядка (в вашем случае это Maybe.of<A> в compose2<A,B,C>).

Вы можете уменьшить это, вручную заполнив типы для compose2 при его использовании.

const safeProp = <T extends Dict, K extends keyof T>(p: K) => (obj: T) => compose2<T, T[K], Maybe<T[K]>>(Maybe.of, prop(p))(obj);

safeProp теперь имеет правильный тип const safeProp: <T extends Dict, K extends keyof T>(p: K) => (obj: T) => Maybe<T[K]>.

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