Общий тип расширенного интерфейса не выводится - PullRequest
0 голосов
/ 23 ноября 2018

В следующем примере Typescript может вывести тип T в методе foo из параметра, переданного ему в bar, но он не выдает тип R, который выглядит такследует - учитывая, что он знает тип T, а также что T extends I<R>

interface I<T> {
}

class A implements I<string> {

}

function foo<T extends I<R>, R>(bar: T): R {
    return;
}

foo(new A());

Есть ли другой способ сделать это?

Ответы [ 3 ]

0 голосов
/ 23 июля 2019

Вы также можете просто извлечь его из реализации.

например.

export class CatWatcher extends Watcher<Cat>
{
    watch(): Cat { ... }  // overridden from base
}

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

Но вы знаете, что у него будет функция watch - поэтому извлеките его оттуда.

export type ExtractWatcherType<T> = T extends { watch: infer W } ? W : never;
0 голосов
/ 07 августа 2019

Я хотел бы добавить принятый ответ от @Titan.Вы также можете добавить скрытый символ на интерфейс, чтобы обмануть структурную типизацию.

Я использовал следующее в своем проекте, и компилятор может вывести тип результата моего действия.Вы можете попробовать это на Typescript Playground .

const F = Symbol();

interface Action<R = unknown> {
    [F]?: R; // this trick the compiler
}

type ActionResult<T extends Action> = T extends Action<infer R> ? R : never;

interface SetName extends Action<SetNameResult> {
    type: 'set_name';
    name: string;
}

interface SetNameResult {
    type: 'set_name_result';
    newName: string;
}

function process<A extends Action>(action: A): ActionResult<A> {
    return {} as any; // TODO process action and return a valid result
}

const r = process({ type: 'set_name', name: 'the name' } as SetName);
r.type; // 'set_name_result'
r.newName; // string
0 голосов
/ 23 ноября 2018

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

interface I<T> { }
declare let foo: I<string> 
declare let bar: I<number>
// Same structure ({}), assignment works
foo = bar
bar = foo 

Событие, если мы добавим поле, Typescript все равно не будет выводить параметр типа R, он просто не будет пытаться извлечь его из T.Лучше всего использовать условный тип и извлечь параметр универсального типа там, где он вам нужен:

interface I<T> {
    value :T 
}

class A implements I<string> {
    value! :string 
}

type ExtractFromI<T extends I<unknown>> = T extends I<infer U> ? U : never;
function foo<T extends I<unknown>>(bar: T):  ExtractFromI<T>{
    return bar.value as ExtractFromI<T>; // Generic type with unresolved type parameters, we need a type assertion to convince the compiler this is ok 
}
var r = foo(new A()); // string
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...