未推断出扩展接口的通用类型
Generic type of extended interface not inferred
在下面的示例中,Typescript 可以从 bar
中传递给它的参数推断方法 foo
中 T
的类型,但它不会推断 foo
的类型=14=],感觉应该是这样——因为它知道 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());
还有其他方法吗?
第一个问题是你的接口是空的,打字稿使用结构类型,所以如果你的通用接口不使用它的类型参数,它有它并不重要。例如,这有效:
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
您可能还只想从实现中提取它。
例如。
export class CatWatcher extends Watcher<Cat>
{
watch(): Cat { ... } // overridden from base
}
我想从 CatWatcher
得到 Cat
。正如其他答案所说,似乎不可能通过推理从基础 class 中提取它。
但你知道它会有一个 watch
功能 - 所以从那里提取它。
export type ExtractWatcherType<T> = T extends { watch: infer W } ? W : never;
我想补充一下@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
在下面的示例中,Typescript 可以从 bar
中传递给它的参数推断方法 foo
中 T
的类型,但它不会推断 foo
的类型=14=],感觉应该是这样——因为它知道 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());
还有其他方法吗?
第一个问题是你的接口是空的,打字稿使用结构类型,所以如果你的通用接口不使用它的类型参数,它有它并不重要。例如,这有效:
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
您可能还只想从实现中提取它。
例如。
export class CatWatcher extends Watcher<Cat>
{
watch(): Cat { ... } // overridden from base
}
我想从 CatWatcher
得到 Cat
。正如其他答案所说,似乎不可能通过推理从基础 class 中提取它。
但你知道它会有一个 watch
功能 - 所以从那里提取它。
export type ExtractWatcherType<T> = T extends { watch: infer W } ? W : never;
我想补充一下@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