typescript - 泛型类的构造函数中可选字段的默认值
问题描述
我试图弄清楚是否有可能(仅使用类型声明)以某种方式适合可选字段的具体默认值——在泛型类的上下文中,这样这个默认值的类型仍然与另一个相关联必填项目
type Handler<T> = (msg: T) => boolean;
type Transformer<T> = (msg: SimpleMsg) => T;
class Consumer<T> {
handler: Handler<T>;
transformer: Transformer<T>;
constructor(handler: Handler<T>, transformer?: Transformer<T>) {
this.handler = handler;
this.transformer = transformer || defaultTransformer
}
}
默认转换器可以是这样的(只是传递值):
const defaultTransformer = (msg: SimpleMsg) => {
console.log('BoringTranfomer! ' + JSON.stringify(msg));
return msg;
}
目前它(正确地)警告我T
可以用与无关的类型实例化SimpleMsg
因此,我会 - 以某种方式想根据变压器的(类型?)定义处理程序的类型(反之亦然?) - 并强制该类型SimpleMsg
以防变压器未定义(即未提供)
我知道可以使用工厂方法或其他方法来解决它,我将处理程序明确定义为Handler<SimpleMsg>
,但我真的想知道它是否可以通过类型和单个入口点解决
谢谢!
解决方案
ATransformer
返回T
,所以我们不可能在不知道是什么的情况下创建默认值T
。但是你在这里有正确的想法:
并强制该类型为 SimpleMsg 以防变压器未定义(即未提供)
构造函数重载
我们可以通过constructor
使用多个参数类型重载函数来做到这一点。我们允许任何匹配的handler
和transformer
函数对同一个T
OR 只是 ahandler
需要 a SimpleMsg
。Typescript 在第二种情况下无法推断出的类型T
并且会返回Consumer<unknown>
,所以我们必须将T
类的默认值设置为SimpleMsg
。
构造函数的主体只知道实现签名中的类型,这与您之前的签名相同。所以我们确实需要断言defaultTransformer
是正确的类型。
(我重命名Transformer
为MsgTransformer
以避免出现重复的类型错误)
class Consumer<T = SimpleMsg> {
handler: Handler<T>;
transformer: MsgTransformer<T>;
// if a transformer is provided, it must match the handler
constructor(handler: Handler<T>, transformer: MsgTransformer<T>)
// if no transformer is provided, then the handler must be for type SimpleMsg
constructor(handler: Handler<SimpleMsg>)
// implementation signature which combines all overloads
constructor(handler: Handler<T>, transformer?: MsgTransformer<T>) {
this.handler = handler;
this.transformer = transformer || defaultTransformer as MsgTransformer<T>;
}
}
测试用例:
// CAN pass just a handler for a SimpleMsg
const a = new Consumer((msg: SimpleMsg) => true); // <SimpleMsg>
const b = new Consumer(() => true); // <SimpleMsg>
// CANNOT pass just a handler for another type
const c = new Consumer((msg: { something: string }) => true); // error as expected
// CAN pass a handler and a transformer that match
const d = new Consumer((msg: { something: string }) => true, (msg: SimpleMsg) => ({ something: "" })); // generic <{something: string}>
// CANNOT have mismatch between handler and transformer
const e = new Consumer((msg: { something: string }) => true, (msg: SimpleMsg) => ({})); // error as expected
编辑:键入选项对象
如果handler
andtransformer
是同一个对象的两个属性,我们不会使用重载。我们将为参数创建一个更复杂的类型。
constructor({handler, transformer}: Options<T>) {
我们可以使用条件类型,transformer
仅当SimpleMsg
返回的defaultTransformer
可分配给时才成为可选类型T
。
type Options<T> = SimpleMsg extends T ? {
handler: Handler<T>;
transformer?: MsgTransformer<T>; // make optional
} : {
handler: Handler<T>;
transformer: MsgTransformer<T>;
}
或者我们可以使用联合类型。这不太安全,因为您可以在传递( )T
时手动声明为任意类型。但这似乎不太可能成为问题。Handler<SimpleMsg>
new Consumer<SomeWrongType>(options)
type Options<T> = {
handler: Handler<T>;
transformer: MsgTransformer<T>;
} | {
handler: Handler<SimpleMsg>;
transformer?: never; // need this in order to destructure
}
推荐阅读
- postgresql - Postgres会话变量在函数内不起作用
- docker - Docker:如何使用 DockerFile 一个接一个地执行 2 个脚本
- typescript - 如何正确转换 typescript-transformer 函数中的“this”关键字?
- ms-access - 公式(德语)与表格(英语)
- java - SpringBoot BeanCreationException:创建名为“requestMappingHandlerAdapter”的bean时出错原因:java.lang.NoSuchFieldError:defaultInstance
- php - 如何修复laravel中的“遇到格式不正确的数值”错误?
- node.js - 谁能解释这个问题?
- nock - Nock - 获取匹配的主机名
- c# - Power Bi API:获取 Power BI 数据源数据
- android - 在壁画中传递自定义标头(sessionid)