我有一个提供decode
功能的界面
interface Converter<T> {
uuid: CUUID;
decode: (value: T) => Buffer;
encode?: (value: Buffer) => T;
}
不,我有一个名为Service
的类,该类提供的对象包含任意数量的这些Converter:
type Converters = { name: Converter<any> };
class Service<C extends Converters> {
}
这样我就可以将这类对象传递给Service
:
interface MyConverters extends Converters {
state: Converter<string>;
water: Converter<number>;
}
const service = new Service<MyConverters>();
Service
类具有几个应在Converters
和通用T
(Converter<T>
)上运行的功能,传递给转换器。因此,不要这样做:
class Service<C extends Converters> {
public write(name: string, value: any): void {
}
}
我希望name
是Converters
中的任何键,而value
是相应{{1}的ReturnType
函数的decode
}即Converter
(所以基本上就是Converters[name]
的{{1}})。
这是我最终想出的:
<T>
…但是它在不起作用
Converter<T>
我遇到了错误
interface Converter<T> { uuid: CUUID; decode: (value: T) => Buffer; encode?: (value: Buffer) => T; } type Converters = { name: Converter<any> }; type ConverterNames<C extends Converters> = keyof C; type ConverterValue<C extends Converters, N extends Keys<C>> = ReturnType<C[N]["decode"]>; class Service<C extends Converters> { public write<N extends ConverterNames<C>>( name: N, value: ConverterValue<C, N> ): void {} }
我不确定发生了什么。
最终我想做到这一点:
type ConverterValue<C extends Converters, N extends ConverterNames<C>> = ReturnType<C[N]["decode"]>;
^^^^^^^^^^^^^^
答案 0 :(得分:2)
在开始之前有几件事:
type Converters = { name: Converter<any> };
不是键映射到Converter<any>
的对象,而是一个键name
映射到Converter<any>
的对象,我怀疑您想要type Converters = { [name: string]: Converter<any>; }
之类的东西,但这也不起作用。参见下文。{ [name: string]: Converter<any>; }
,因为任何扩展类型都需要提供索引签名(即允许访问任何字符串,而不是您想要的。我遇到的最好的是
interface Converter<T> {
uuid: CUUID;
decode: (value: T) => Buffer;
encode?: (value: Buffer) => T;
}
class Service<C> {
public write<K extends keyof C>(
name: K,
value: C[K] extends Converter<infer R> ? R : never
): void { }
}
interface MyConverters {
state: Converter<string>;
water: Converter<number>;
}
const service = new Service<MyConverters>();
service.write('water', 'foo'); // Error, expected number.
简而言之,我放弃extends Converters
来支持条件推断。另外,我是从T
推论Converter<T>
而不是ReturnType<Converter<T>['decoder']>
,看看ReturnType
如何在幕后使用条件推论,这种方法似乎更简单。>
如果您尝试传递不是Converter的密钥,则无论您提供什么值,您的write
调用都不会编译。
答案 1 :(得分:2)
您遇到的第一个问题是Converters
的外观。那只是带有name
类型的Converter<any>
键的类型。您可能想写type Converters = { [name: string]: Converter<any> };
的意思是带有索引签名的类型,这种类型会更接近,但在write
调用中无法按预期工作,因为keyof C
实际上会变成{{1 }},因为该界面可以有任何键。
解决方案是使用string | number
作为约束,其中Record<K, Converter<any>
是我们传入的任何类型的键:
K
修改 @Madara Uchiha♦ConverterValue版本比我的好,我会用它。
interface Converter<T> {
uuid: CUUID;
decode: (value: T) => Buffer;
encode?: (value: Buffer) => T;
}
type Converters<K extends PropertyKey> = Record<K, Converter<any>>
type ConverterValue<C extends Converters<keyof C>, N extends keyof C> = Parameters<C[N]["decode"]>[0];
class Service<C extends Converters<keyof C>> {
public write<N extends keyof C>(
name: N,
value: ConverterValue<C, N>
): void {}
}
interface MyConverters extends Converters<keyof MyConverters> {
state: Converter<string>;
water: Converter<number>;
}
const service = new Service<MyConverters>();
service.write("water", 12);
service.write("water", "12"); // err
service.write("state", "");