我有一系列 API,其中包含各种相关的泛型类型(以及关联的泛型类型约束)。我合成了一个示例并将其粘贴在下面,使用众所周知的类型来模拟一些构造,仅供说明之用。
在使用这些类型时,我经常想从另一个类型创建一个类型,重用引用类型的泛型类型参数。该操作类似于接口(interface)可以根据提供给其定义的泛型类型来约束其属性类型的方式(您将在代码示例中看到,这就是我正在做的解决方法)。
type Properties = { [name: string]: string }
interface SomeGeneric<A,B extends Properties,C extends HTMLElement> {
someConcreteMethod: () => A
someOtherConcreateMethod: (b: B) => void
aConcreteProperty: C
// Fake properties, don't really exist at runtime, just to allow type derivation
// This is obviously terrible. Is there anything better?
relatedInterface?: RelatedInterface<A,B>
anotherRelatedInterface?: AnotherRelatedInterface<A,C>
thirdRelatedInterface?: ThirdRelatedInterface<A,B,C>
}
interface RelatedInterface<A,B extends Properties> {
yetAnotherMethod: (a: A, b: B) => void
}
interface AnotherRelatedInterface<A,C extends HTMLElement> {
oneMoreMethod: (c: C) => A
}
interface ThirdRelatedInterface<A,B extends Properties,C extends HTMLElement> {
thirdMethod: (a: A, b: B, c: C) => () => void
// Fake properties again
relatedInterface?: RelatedInterface<A,B>
anotherRelatedInterface?: AnotherRelatedInterface<A,C>
}
type ConcreteB = { name: string };
interface SomeConcreteType extends SomeGeneric<string,ConcreteB,HTMLDivElement> {}
// So I have a family of related types, all generic, pertaining to one another. In this example, I've simplified it,
// by making one type the canonical type (SomeGeneric). In my domain, that's not really true (you can see below I
//. create a type based on a ThirdRelatedInterface type also), although I could obviously pick one if I had to do that.
//. I've considered trying to base everything on SomeGeneric (and had the other types receive a type parameter that
//. extends SomeGeneric<A,B,C> rather than the type parameters A, B, and C themselves) but because these types all
//. have standalone use cases it would force me to declare useless unneeded type parameters when creating the
//. standalone types.
interface SomeConcreteRelatedInterfaceVerbosely extends RelatedInterface<string,ConcreteB> {}
interface AnotherConcreteRelatedInterfaceVerbosely extends AnotherRelatedInterface<string,HTMLDivElement> {}
interface ThirdConcreteRelatedInterfaceVerbosely extends ThirdRelatedInterface<string,ConcreteB,HTMLDivElement> {}
// This is the only way I've been able to find to derive the related types without repeating the generic type parameters.
//. Because I have a family of related types, they all have synthetic, fake, type-inference-only properties like these.
// Is there a better way?
type SomeConcreteRelatedInterfaceDeclaratively = SomeConcreteType["relatedInterface"]
type AnotherConcreteRelatedTypeDeclaratively = SomeConcreteType["anotherRelatedInterface"]
type ThirdConcreteTypeDeclaratively = SomeConcreteType["thirdRelatedInterface"]
type DerivedFromThirdTypeDeclaratively = ThirdConcreteTypeDeclaratively["anotherRelatedInterface"]
由于多种原因,这显然是可怕的,主要原因是现在编译器认为这些对象具有这些类型的属性,而实际上我只是使用属性“定义”来进行从一种受约束的泛型类型到另一种受约束的泛型类型的转换,我不知道该怎么做。
这似乎与“映射类型”不对应 feature ,这与迭代某种类型的键有关。我在 generics 中也找不到任何内容讨论如何使用类型约束的文档。 (请参阅下面关于一个 SO 问题,该问题使用 infer
从泛型类型中提取类型参数。)
之前的相关问题
有几个问题可以解决 TypeScript 的情况,即有人想要通过约束使一种泛型类型依赖于另一种泛型类型(例如,参见 TypeScript Generic Parameter Type Extending Another Type ),但我还没有成功找到一个处理泛型类型的问题不同类型的相互依赖。
我发现至少直接操作泛型类型参数的一件事是 Typescript: Retrieve element type information from array type ,它说明了如何将 Array
类型转换为其元素的类型(这是通用参数):
export type ArrayElement<ArrayType extends readonly unknown[]> =
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
所以这肯定更类似于我想要做的事情,尽管我想以某种方式将提取的类型进一步转换为我的相关类型之一。我必须承认,我还没有弄清楚 infer
是如何工作的,所以我不太明白如何使用它,或者它是否适用。
This question关于具有基于泛型类型的泛型约束的类型(以及使用泛型类型参数的参数的愿望)也得到了一些稍微类似的东西,但仍然属于单一类型。不过,该解决方案的某些内容可能适用。
最佳答案
不幸的是,目前,在不知道类型本身的情况下,无法获取类型/接口(interface)的泛型参数。因此这个问题不会有完美的解决方案。
一种选择是为与其他接口(interface)相关的每种接口(interface)提供一个泛型类型。我们必须使用infer keyword为了达成这个。您可以解读infer
作为变量声明。
文档示例:
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
简单来说,infer
声明一个类似于占位符的参数,它将评估条件的左侧和右侧:
Array<string> extends Array<infer R>
, R
必须是string
对于方程。我们还可以对推断的参数写一些约束:
infer R extends string
,那么编译器将处理 R
作为string
不是未知参数。
我们将以相同的方式使用它,但使用更多的参数,这根本不会改变逻辑。假设T
延伸SomeGeneric<any, any, any>
:
T extends SomeGeneric<
infer A extends string,
infer B extends Properties,
infer C extends HTMLElement
>
这样,我们就得到了T
的所有三个参数。具有正确的约束( string
、 Properties
、 HTMLElement
)。然后我们可以将它们的参数传递给适当的接口(interface)。
SomeGeneric
的 setter/getter :
type GetSomeGenericRelatedInterfaces<T extends SomeGeneric<any, any, any>> =
T extends SomeGeneric<
infer A extends string,
infer B extends Properties,
infer C extends HTMLElement
>
? {
relatedInterface?: RelatedInterface<A, B>;
anotherRelatedInterface?: AnotherRelatedInterface<A, C>;
thirdRelatedInterface: ThirdRelatedInterface<A, B, C>;
}
: never;
ThirdRelatedInterface
的 setter/getter :
type GetThirdRelatedInterfaces<T extends ThirdRelatedInterface<any, any, any>> =
T extends ThirdRelatedInterface<
infer A extends string,
infer B extends Properties,
infer C extends HTMLElement
>
? {
relatedInterface?: RelatedInterface<A, B>;
anotherRelatedInterface?: AnotherRelatedInterface<A, C>;
}
: never;
通用包装:
type GetRelatedInterfaces<T> = T extends SomeGeneric<any, any, any>
? GetSomeGenericRelatedInterfaces<T>
: T extends ThirdRelatedInterface<any, any, any>
? GetThirdRelatedInterfaces<T>
: never;
用法:
type ConcreteB = { name: string };
interface SomeConcreteType
extends SomeGeneric<string, ConcreteB, HTMLDivElement> {}
interface ThirdConcreteRelatedInterfaceVerbosely
extends ThirdRelatedInterface<string, ConcreteB, HTMLDivElement> {}
// type Case1 = {
// relatedInterface?: RelatedInterface<string, ConcreteB>;
// anotherRelatedInterface?: AnotherRelatedInterface<string, HTMLDivElement>;
// thirdRelatedInterface: ThirdRelatedInterface<
// string,
// ConcreteB,
// HTMLDivElement
// >;
// };
type Case1 = GetRelatedInterfaces<SomeConcreteType>;
// type Case2 = {
// relatedInterface?: RelatedInterface<string, ConcreteB>;
// anotherRelatedInterface?: AnotherRelatedInterface<string, HTMLDivElement>;
// }
type Case2 = GetRelatedInterfaces<ThirdConcreteRelatedInterfaceVerbosely>;
关于typescript - 能否根据泛型参数将泛型 TypeScript 类型映射到另一种类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76258091/