【发布时间】:2021-10-22 08:41:15
【问题描述】:
Expression<T> 是一个可以评估为T 的类:
class Expression<T> {
evaluate(): T {
// ...
}
}
Expression 有一个解析表达式的静态 parse 方法。它的第二个参数表示期望的类型,并确定返回的Expression的泛型类型:
export interface RGBA {
r: number;
g: number;
b: number;
a: number;
}
interface TypeMap {
string: string;
number: number;
color: RGBA;
boolean: boolean;
[other: string]: any;
}
export class Expression<T> {
static parse<T extends expression.StylePropertyType>(
expr: number | string | Readonly<StyleFunction> | Readonly<MapboxExpression>,
expectedType?: T,
): Expression<TypeMap[T]> {
// ...
}
}
(完整要点here,上下文为Mapbox style expressions)。
这使您可以解析表达式并以类型安全的方式评估它们:
const colorExp = Expression.parse("red", "color"); // type is Expression<RGBA>
const red = colorExp.evaluate(); // type is RGBA
const numExp = Expression.parse(["+", 1, 2], "number"); // type is Expression<number>
const num = numExp.evaluate(); // type is number
到目前为止一切顺利。现在我想编写一个包装Expression.parse 的函数来传递undefined 值。没有类型的实现很简单:
const parseOrUndef = (expr, type) => expr === undefined ? undefined : Expression.parse(expr, type);
但是如果我想要这些类型呢?理想情况下,我会:
const colorExp = parseOrUndef("red", "color"); // type is Expression<RGBA> | undefined
const red = colorExp?.evaluate(); // type is RGBA | undefined
const numExp = parseOrUndef(["+", 1, 2], "number"); // type is Expression<number> | undefined
const num = numExp.evaluate(); // type is number | undefined
是否可以在 TypeScript 中执行此操作而无需复制/粘贴 Expression.parse 中的所有类型?这是一次尝试:
type AddUndefToTuple<T extends any[]> = T extends [infer First, ...infer Rest]
? [First | undefined, ...Rest]
: [];
const withUndef = <Fn extends (...args: any[]) => any>(fn: Fn) => (
...args: AddUndefToTuple<Parameters<Fn>>
): ReturnType<Fn> | undefined => ((args as any)[0] === undefined ? undefined : fn(...args));
const parseOrUndef = withUndef(Expression.parse);
不幸的是,这会导致Expression<any>,因为当您将Fn 拆分为Parameters<Fn> 和Return<Fn> 时,type 参数和返回类型之间的关系会丢失:
const colorExp = parseOrUndef("red", "color"); // type is Expression<any> | undefined
const red = colorExp?.evaluate(); // type is any :(
const numExp = parseOrUndef(["+", 1, 2], "number"); // type is Expression<any> | undefined
const num = numExp?.evaluate(); // type is any :(
有没有办法在不失去这种关系的情况下通用地做到这一点?还是我必须从Expression.parse 复制/粘贴类型?
【问题讨论】:
标签: typescript