keyof не удовлетворяет ограничению при вложении

#typescript #typescript-generics

#typescript #typescript-дженерики

Вопрос:

Использование расширения keyof некоторого типа объекта отлично работает для индексации этого типа, но не работает с расширением типа объекта. Есть ли способ сообщить TypeScript, что типы должны работать, это ошибка или я что-то упускаю?

 type Foo = { bar(a: string): boolean }

// works
type Thing1<F extends keyof Foo> = ReturnType<Foo[F]>

// doesn't work:
type Thing2<Fo extends Foo, F extends keyof Fo> = ReturnType<Fo[F]>
 

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

ошибка ts:

 Type 'Fo[F]' does not satisfy the constraint '(...args: any) => any'.
  Type 'Fo[keyof Fo]' is not assignable to type '(...args: any) => any'.
    Type 'Fo[string] | Fo[number] | Fo[symbol]' is not assignable to type '(...args: any) => any'.
      Type 'Fo[string]' is not assignable to type '(...args: any) => any'.(2344)
 

Вот что я пытаюсь сделать: ссылка на игровую площадку

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

1. Что не так с type Thing2<Fo extends Foo, F extends keyof Foo> = ReturnType<Fo[F]> ? У вас есть сценарий, который не работает в этом случае?

2. Возможно, я вас неправильно понимаю, но, согласно демонстрации playground, это не печатается. TypeScript выдает ошибку.

3. Делает для меня, typescriptlang.org/play ? #code/ …

4. ах, я пропустил ваш дополнительный o в Foo.

5. Я предполагаю, что TypeScript ничего не знает о возможных ключах Fo , поэтому он не позволит вам использовать его во втором ограничении.

Ответ №1:

Компилятор TypeScript понятия не имеет, какие ключи находятся на чем-то, что расширяет Foo, и все ли они являются функциями с возвращаемым типом, поэтому if не позволит вам это сделать.

Взгляните на этот пример, Foo это нормально, потому что у него есть ключи метода, Foo2 не потому, что у него есть ключ, не являющийся ключом метода, и Foo3 все в порядке, потому что у него также есть все ключи метода. Foo2 и Foo3 являются продолжениями Foo

 export type Foo = { bar(a: string): boolean; }

export type Foo2 = Foo amp; { value: string }

export type Foo3 = Foo amp; { bar2(a: number): string; }

// works
type Thing1<F extends keyof Foo> = ReturnType<Foo[F]>

// error!
type Thing2<F extends keyof Foo2> = ReturnType<Foo2[F]>

// works
type Thing3<F extends keyof Foo3> = ReturnType<Foo3[F]>
 

Если вы попытаетесь ввести противопоставление ключей расширению, вы можете легко передать Foo2 и ключ, на Foo2 котором не было бы a ReturnValue .

Вот причина, по которой ваш пример возвращает ошибку типа.

Как я уже говорил в комментариях, вы можете ограничить свои ключи основным типом, и он будет работать.

 type Thing3<Fo extends Foo, F extends keyof Foo> = ReturnType<Fo[F]>
 

Однако вы можете заставить это работать, добавив другое ограничение, в котором говорится, что все ключи объекта должны быть методом. Вам даже не нужен исходный тип.

 type Thing<Foo extends Record<Bar, (...args: any) => any>, Bar extends keyof Foo> = ReturnType<Foo[Bar]>
 

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

1. спасибо за объяснение! добавлена ссылка на игровую площадку к исходному вопросу, иллюстрирующая, что я пытаюсь сделать и где она ломается

2. Круто, я посмотрю и посмотрю, смогу ли я помочь.

3. вау, я думаю, что ваше последнее предложение с другим ограничением может быть решением! действительно аккуратно … не придумал бы этого!

4. Если у вас все еще возникают проблемы, дайте мне знать, каков ожидаемый результат вашей функции reduce, и я посмотрю еще раз.

5. У вас есть какие-либо идеи, почему это не работает: gist.github.com/willium/0e4aa3d24ba52080a2a73e7367519b19