gpt4 book ai didi

typescript - 通用联合类型执行交集类型的棘手输入问题

转载 作者:行者123 更新时间:2023-12-05 04:19:47 26 4
gpt4 key购买 nike

我偶然发现了一个奇怪的问题,我不知道如何解决这个问题。我有一个泛型类。此类包含一个只有一个联合参数的方法,要求泛型类型或对象。由于此类使用不同类型多次实例化,我想创建一个通用方法来检索其中一个实例(通用类型),并调用其中的方法。

复制代码如下:

export interface A {
a: any;
}
export interface B {
b: any;
}
export interface MyClasses {
a: MyClass<A>;
b: MyClass<B>;
}

export interface C {
c: any;
}
export declare class MyClass<T = { [prop: string]: any }> {
myMethod(data: T | C): T;
}

export type MyClassesKeys = keyof MyClasses;
export type MyClassInferGenericType<T> = T extends MyClass<infer G> ? G : T;

export class MyService {
private myClasses: MyClasses = {
a: new MyClass<A>(),
b: new MyClass<B>()
};

public getMyClass<T extends MyClassesKeys>(keyName: T): MyClasses[T] {
return this.myClasses[keyName];
}

public callerMethod<T extends MyClassesKeys, U extends MyClassInferGenericType<MyClasses[T]>>(key: T, myData: U) {
// This part is usually retrieved gener
const myClassInstance = this.getMyClass(key);

// My call
myClassInstance.myMethod(myData);
}
}

这会以某种方式返回编译错误:

Argument of type 'A | B' is not assignable to parameter of type '(A | C) & (B | C)'.
Type 'A' is not assignable to type '(A | C) & (B | C)'.
Type 'A' is not assignable to type 'A & C'.
Property 'c' is missing in type 'A' but required in type 'C'.

由于“myClassInstance”是 MyClasses[T] 类型,而 myData 采用与 MyClasses[T] 关联的泛型类型,因此应该可以正确推断出所有内容。

那么,为什么 typescript 会尝试对我的类型进行交集?

最佳答案

你得到交集的原因是因为编译器忘记了 key 类型之间的相关性和 myData 的类型,所以它只接受它知道是安全的东西。如果myData 都是 A B (如 {a: 1, b: 2} ),那么调用 myClassInstance.myMethod(myData) 是安全的无论哪个key被传入。

之所以会这样,是因为 myClassInstance.myMethod看到有一个 union type :

const x = myClassInstance.myMethod;
// const x: ((data: A | C) => A) | ((data: B | C) => B)

如果你调用函数联合,you have to pass in an intersection of its parameters让编译器接受它。

当然不可能有人传myDatakey没有正确匹配。 (实际上这不是真的,因为 key 可能是一个联合类型。没有 ms/TS#27808 它是可能的。假设它是可能的但不太可能。或者至少我们不会担心它在这里。)但是编译器看不到它。

编译器并不真正支持查看不同类型之间任意复杂的相关性。 microsoft/TypeScript#30581 中描述了基本问题它根据相关联合构建它。


幸运的是,通常有一种重构类型的方法,使编译器能够遵循逻辑。这在 microsoft/TypeScript#47109 中有描述。 .这个想法是制作一个尽可能简单地表示您的输入/输出关系的“基本”类型:

interface MyClassGenerics {
a: A;
b: B
}

然后我们写MyClasses明确地作为 mapped type在那个基本类型上:

type MyClasses = {
[K in keyof MyClassGenerics]: MyClass<MyClassGenerics[K]>
}

所以现在,当你写 MyClasses[K] , 编译器会自动看到它与 MyClass<MyClassGenerics[K]> 的关系.可以引用MyClassGenerics[K]无需使用 MyClassInferGenericType :

public callerMethod<K extends MyClassesKeys>(key: K, myData: MyClassGenerics[K]) {
const myClassInstance = this.getMyClass(key);

const x = myClassInstance.myMethod
// const x: (data: C | MyClassGenerics[K]) => MyClassGenerics[K]

myClassInstance.myMethod(myData); // okay
}

现在 myClassInstance.myMethod 的类型被视为单一函数类型,其输入和输出类型取决于泛型类型参数 K .它不再是联合,因此可以更轻松地调用它。

Playground link to code

关于typescript - 通用联合类型执行交集类型的棘手输入问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74680936/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com