基于第一种类型有条件地提供第二种泛型类型
Provide a second generic type conditionally based on the first type
我正在尝试为 mongoose
生成一个 class 定义,其中我想使用第一个泛型到 select 第二个泛型。一个简化的概念版本看起来像这样:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number;
constructor(val: number) {
this.val = val;
}
}
class test<A extends string | number, B extends typeof StringClass | typeof NumberClass) {
val: B;
constructor(val: A, Class: B) {
this.val = new Class(val);
}
}
我想将测试定义为:
class test<A extends string | number, B extends (A instanceof string ? typeof StringClass : typeof NumberClass)) {
val: B;
constructor(val: A, Class: B) {
this.val = new Class(val);
}
}
这在 Typescript 4.4 中无效,因此我想知道,如何使第二个泛型依赖于第一个参数?
为什么会出现这个错误?
这里有一个例子:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
class test<A extends string | number, B extends Check<A>> {
val: B;
constructor(val: A, Class: B) {
// Type 'string' is not assignable to type 'never'
this.val = new Class(val);
}
}
const result = new test('2', StringClass)
在上述情况下 val
推断为 never
,因为
multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.
因此string & number === never
C考虑这个例子:
class StringClass {
val: { string: string };
constructor(val: { string: string }) {
this.val = val;
}
}
class NumberClass {
val: { number: number }
constructor(val: { number: number }) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
class test<A extends string|number, B extends Check<A>> {
val: B;
constructor(val: A, Class: B) {
// Type 'string' is not assignable to type '{ string: string; } & { number: number; }'
this.val = new Class(val);
}
}
const result = new test('2', StringClass)
val
是 { string: string; } & { number: number; }
.
那么,如何解决呢?
您可以摆脱泛型并通过适当的限制重载您的构造函数
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
interface Overloading {
new(val: string): any
new(val: number): any
new(val: string | number): any
}
class test {
val: StringClass | NumberClass
constructor(val: number, Class: typeof NumberClass)
constructor(val: string, Class: typeof StringClass)
constructor(val: never, Class: typeof StringClass & typeof NumberClass) {
this.val = new Class(val)
}
}
const _ = new test('2', StringClass) // ok
const __ = new test(2, StringClass) // expected error
一般来说,运行时值不能依赖于通用条件(参见 Check
)。不安全。
这就是为什么在这里:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
class test<A, B extends {
0: typeof StringClass,
1: typeof NumberClass,
2: never
}[A extends string ? 0 : A extends number ? 1 : 2]> {
val: B
constructor(val: A, Class: B) {
this.val = new Class(val) // error
}
}
你有一个错误。
在这种情况下,您始终可以使用类型断言 as never
:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
class test<A, B extends {
0: typeof StringClass,
1: typeof NumberClass,
2: never
}[A extends string ? 0 : A extends number ? 1 : 2]> {
val: StringClass | NumberClass
constructor(val: A, Class: B) {
this.val = new Class(val as never) // type asserion
}
}
const _ = new test('2', StringClass) // ok
const __ = new test(2, StringClass) // expected error
在这里使用类型断言安全吗?我不确定。我认为这取决于你。如果此代码仅用于测试 - 请像我一样使用 as never
或 never
。
如果这是生产代码 - 请使用条件语句
我正在尝试为 mongoose
生成一个 class 定义,其中我想使用第一个泛型到 select 第二个泛型。一个简化的概念版本看起来像这样:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number;
constructor(val: number) {
this.val = val;
}
}
class test<A extends string | number, B extends typeof StringClass | typeof NumberClass) {
val: B;
constructor(val: A, Class: B) {
this.val = new Class(val);
}
}
我想将测试定义为:
class test<A extends string | number, B extends (A instanceof string ? typeof StringClass : typeof NumberClass)) {
val: B;
constructor(val: A, Class: B) {
this.val = new Class(val);
}
}
这在 Typescript 4.4 中无效,因此我想知道,如何使第二个泛型依赖于第一个参数?
为什么会出现这个错误? 这里有一个例子:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
class test<A extends string | number, B extends Check<A>> {
val: B;
constructor(val: A, Class: B) {
// Type 'string' is not assignable to type 'never'
this.val = new Class(val);
}
}
const result = new test('2', StringClass)
在上述情况下 val
推断为 never
,因为
multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.
因此string & number === never
C考虑这个例子:
class StringClass {
val: { string: string };
constructor(val: { string: string }) {
this.val = val;
}
}
class NumberClass {
val: { number: number }
constructor(val: { number: number }) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
class test<A extends string|number, B extends Check<A>> {
val: B;
constructor(val: A, Class: B) {
// Type 'string' is not assignable to type '{ string: string; } & { number: number; }'
this.val = new Class(val);
}
}
const result = new test('2', StringClass)
val
是 { string: string; } & { number: number; }
.
那么,如何解决呢?
您可以摆脱泛型并通过适当的限制重载您的构造函数
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
type Check<A> = A extends string ? typeof StringClass : typeof NumberClass
interface Overloading {
new(val: string): any
new(val: number): any
new(val: string | number): any
}
class test {
val: StringClass | NumberClass
constructor(val: number, Class: typeof NumberClass)
constructor(val: string, Class: typeof StringClass)
constructor(val: never, Class: typeof StringClass & typeof NumberClass) {
this.val = new Class(val)
}
}
const _ = new test('2', StringClass) // ok
const __ = new test(2, StringClass) // expected error
一般来说,运行时值不能依赖于通用条件(参见 Check
)。不安全。
这就是为什么在这里:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
class test<A, B extends {
0: typeof StringClass,
1: typeof NumberClass,
2: never
}[A extends string ? 0 : A extends number ? 1 : 2]> {
val: B
constructor(val: A, Class: B) {
this.val = new Class(val) // error
}
}
你有一个错误。
在这种情况下,您始终可以使用类型断言 as never
:
class StringClass {
val: string;
constructor(val: string) {
this.val = val;
}
}
class NumberClass {
val: number
constructor(val: number) {
this.val = val;
}
}
class test<A, B extends {
0: typeof StringClass,
1: typeof NumberClass,
2: never
}[A extends string ? 0 : A extends number ? 1 : 2]> {
val: StringClass | NumberClass
constructor(val: A, Class: B) {
this.val = new Class(val as never) // type asserion
}
}
const _ = new test('2', StringClass) // ok
const __ = new test(2, StringClass) // expected error
在这里使用类型断言安全吗?我不确定。我认为这取决于你。如果此代码仅用于测试 - 请像我一样使用 as never
或 never
。
如果这是生产代码 - 请使用条件语句