泛型构造函数中可选字段的默认值 class
Default value for optional field in a constructor of a generic class
我试图弄清楚是否有可能(单独使用类型声明)以某种方式适合可选字段的具体默认值 --- 在泛型 class 的上下文中,这样类型此默认值的一部分仍将绑定到另一个必填字段
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>
,但我真的想知道它是否可以通过类型和单个入口点解决
谢谢!
A Transformer
returns T
所以我们不可能在不知道 T
是什么的情况下创建默认值。但是你的想法是对的:
and force that type to be SimpleMsg in case the transformer is undefined (i.e. not provided)
构造函数重载
我们可以通过使用多个参数类型重载 constructor
函数来做到这一点。我们允许任何匹配的 handler
和 transformer
函数对用于相同的 T
或者只是 handler
接受 SimpleMsg
。在第二种情况下,TypeScript 无法推断出 T
的类型,并且会 return Consumer<unknown>
,因此我们必须将 class 的 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
和 transformer
是同一对象的两个属性的情况下,我们不会使用重载。我们只会为参数创建一个更复杂的类型。
constructor({handler, transformer}: Options<T>) {
我们可以使用条件类型,只有当 SimpleMsg
return 为 defaultTransformer
编辑可分配给 T
时,才使 transformer
可选。
type Options<T> = SimpleMsg extends T ? {
handler: Handler<T>;
transformer?: MsgTransformer<T>; // make optional
} : {
handler: Handler<T>;
transformer: MsgTransformer<T>;
}
或者我们可以使用联合类型。这不是类型安全的,因为您可以在传递 Handler<SimpleMsg>
(new Consumer<SomeWrongType>(options)
) 时手动将 T
声明为任意类型。但这似乎不太可能成为问题。
type Options<T> = {
handler: Handler<T>;
transformer: MsgTransformer<T>;
} | {
handler: Handler<SimpleMsg>;
transformer?: never; // need this in order to destructure
}
我试图弄清楚是否有可能(单独使用类型声明)以某种方式适合可选字段的具体默认值 --- 在泛型 class 的上下文中,这样类型此默认值的一部分仍将绑定到另一个必填字段
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>
,但我真的想知道它是否可以通过类型和单个入口点解决
谢谢!
A Transformer
returns T
所以我们不可能在不知道 T
是什么的情况下创建默认值。但是你的想法是对的:
and force that type to be SimpleMsg in case the transformer is undefined (i.e. not provided)
构造函数重载
我们可以通过使用多个参数类型重载 constructor
函数来做到这一点。我们允许任何匹配的 handler
和 transformer
函数对用于相同的 T
或者只是 handler
接受 SimpleMsg
。在第二种情况下,TypeScript 无法推断出 T
的类型,并且会 return Consumer<unknown>
,因此我们必须将 class 的 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
和 transformer
是同一对象的两个属性的情况下,我们不会使用重载。我们只会为参数创建一个更复杂的类型。
constructor({handler, transformer}: Options<T>) {
我们可以使用条件类型,只有当 SimpleMsg
return 为 defaultTransformer
编辑可分配给 T
时,才使 transformer
可选。
type Options<T> = SimpleMsg extends T ? {
handler: Handler<T>;
transformer?: MsgTransformer<T>; // make optional
} : {
handler: Handler<T>;
transformer: MsgTransformer<T>;
}
或者我们可以使用联合类型。这不是类型安全的,因为您可以在传递 Handler<SimpleMsg>
(new Consumer<SomeWrongType>(options)
) 时手动将 T
声明为任意类型。但这似乎不太可能成为问题。
type Options<T> = {
handler: Handler<T>;
transformer: MsgTransformer<T>;
} | {
handler: Handler<SimpleMsg>;
transformer?: never; // need this in order to destructure
}