类似于TypeScript Mapped Types:获取数组的元素类型,我有一个类型,比如:
type A = {
Item1: Promise<string>,
Item2: Promise<number>,
Item3: number
}
我想从中提取以下类型:
type A' = {
Item1: string,
Item2: number,
Item3: number
}
请注意,其中一个字段不是承诺增加了复杂性。
这甚至可能吗,或者我只是达到了打字稿推断类型的极限?我尝试摆弄映射类型和记录类型,但我就是想不通。
更新:
假设我想对函数调用做同样的事情:
type A = {
Item1: (string, number) => Promise<string>,
Item2: () => Promise<number>,
Item3: () => number
}
所需的类型是:
type A' = {
Item1: (string, number) => string,
Item2: () => number,
Item3: () => number
}
我认为这与我陈述的第一个情况非常相似,但函数返回值似乎并不像我希望的那样简单。
更新 2020-01-28
自从 TypeScript 2.8 引入了条件类型以来,您现在可以相对轻松地进行此映射:
type A = {
Item1: (x: string, y: number) => Promise<string>,
Item2: () => Promise<number>,
Item3: () => number,
Item4: Promise<string>,
Item5: Promise<number>,
Item6: number
}
type UnpromiseObj<T> = { [K in keyof T]: T[K] extends Promise<infer U> ? U :
T[K] extends (...args: infer A) => Promise<infer R> ? (...args: A) => R :
T[K]
}
type Aprime = UnpromiseObj<A>;
/* type Aprime = {
Item1: (x: string, y: number) => string;
Item2: () => number;
Item3: () => number;
Item4: string;
Item5: number;
Item6: number;
} */
我将保留以下内容,以便您可以为后代保留在条件类型存在之前必须经历的疯狂废话:
结束更新 2020-01-28
这并非完全不可能,但是如果没有官方对映射条件类型之类的支持,这是不确定的。让我们试试。 首先,让我们设置一些类型级布尔逻辑:
type False = '0';
type True = '1';
type Bool = False | True;
type If<Cond extends Bool, Then, Else> = { '0': Else; '1': Then }[Cond];
所以类型If<True, Then, Else>
的计算结果为Then
,而类型If<False, Then, Else>
的计算结果为Else
。
第一个问题是您需要能够确定类型是否为Promise
。 第二个是你需要能够得到T
的类型 ,给定一个Promise<T>
. 为此,我将通过使用一些在运行时不存在的幻像属性来增强Object
和Promise<T>
接口的声明:
// if you are in a module you need to surround
// the following section with "declare global {}"
interface Object {
"**IsPromise**": False
}
interface Promise<T> {
"**IsPromise**": True
"**PromiseType**": T
}
这是问题所在。 扩充全局接口不是很好,因为它们在每个人的命名空间中。 但它具有所需的行为:任何不是Promise
的Object
都具有其"**IsPromise**"
属性的False
类型,并且Promise
具有True
值。 此外,Promise<T>
具有类型T
的"**PromiseType**"
属性。 同样,这些属性在运行时不存在,它们只是为了帮助编译器。
现在我们可以定义Unpromise
将Promise<T>
映射到T
,并保留任何其他类型:
type Unpromise<T extends any> = If<T['**IsPromise**'], T['**PromiseType**'], T>
MapUnpromise
将Unpromise
映射到对象的属性:
type MapUnpromise<T> = {
[K in keyof T]: Unpromise<T[K]>
}
让我们看看它是否有效:
type A = {
Item1: Promise<string>,
Item2: Promise<number>,
Item3: number
}
type Aprime = MapUnpromise<A>
// evaluates to { Item1: string; Item2: number; Item3: number; }
成功! 但是我们已经做了一些相当不愉快的事情来让它发生,这可能不值得。 这取决于你!
希望有帮助;祝你好运!
<小时 />更新 1
不幸的是,据我所知,对函数调用做同样的事情是不可能的。 你真的需要一个扩展的typeof
类型查询,而它现在不是TypeScript的一部分(无论如何,从TypeScript v2.5开始)。
因此,您不能获取类型A
并从中计算APrime
(请注意,A'
不是有效的标识符。 如果需要,请使用Aʹ
)。 但是你可以创建一个基本类型,从中可以计算A
和APrime
:
type False = '0';
type True = '1';
type Bool = False | True;
type If<Cond extends Bool, Then, Else> = { '0': Else; '1': Then }[Cond];
type MaybePromise<Cond extends Bool, T> = If<Cond, Promise<T>, T>
我已经放弃了全局增强并添加了MaybePromise<Cond, T>
,其中MaybePromise<True, T>
评估为Promise<T>
,MaybePromise<False, T>
评估为T
。 现在我们可以使用以下MaybePromise<>
获取A
和APrime
:
type ABase<Cond extends Bool> = {
Item1: (s: string, n: number) => MaybePromise<Cond, string>,
Item2: () => MaybePromise<Cond, number>,
Item3: () => number
}
type A = ABase<True>;
// evaluates to {
// Item1: (s: string, n: number) => Promise<string>;
// Item2: () => Promise<number>;
// Item3: () => number; }
type APrime = ABase<False>;
// evaluates to {
// Item1: (s: string, n: number) => string;
// Item2: () => number;
// Item3: () => number; }
所以这行得通! 但是我建议的重构可能不适合您的用例。 这取决于您首先如何获取A
类型。 哦,好吧,这是我能做的最好的事情。 希望它有所帮助。 再次祝你好运!
现在使用TypeScript 4.5 的Awaited
实用程序类型变得更容易了:
type A = {
Item1: Promise<string>;
Item2: Promise<number>;
Item3: number;
Item4: (arg0: string, arg1: number) => Promise<string>;
Item5: () => Promise<number>;
Item6: () => number;
};
type AwaitValues<T> = {
[K in keyof T]: T[K] extends (...args: infer P) => infer R
? (...args: P) => Awaited<R>
: Awaited<T[K]>;
};
type A2 = AwaitValues<A>;
通过悬停A2
我们可以看到它的类型是:
/*
type A2 = {
Item1: string;
Item2: number;
Item3: number;
Item4: (arg0: string, arg1: number) => string;
Item5: () => number;
Item6: () => number;
}
*/