typescript4.0 - 强制进行良好的 TypeScript 编译时验证错误
TypeScript 语言允许进行编译时验证。让我们通过以下示例来演示(也提供 Playground)
// Some complex compile-time type check, for instance, ariphmetic expression validator
type NumericSymbols = `0` | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type OperationSymbols = '+' | '-' | '/' | '*'
type Integers<T extends string> = T extends '' ? never : T extends NumericSymbols ? T : T extends `${infer _FirstLetter}${infer Rest}` ? _FirstLetter extends NumericSymbols
? `${_FirstLetter}${Integers<Rest>}` : never : never
type Operation<T extends string> = T extends `${infer leftExpression}${OperationSymbols}${infer rightExpression}` | `(${infer leftExpression}${OperationSymbols}${infer rightExpression})`
? Operation<leftExpression> | Integers<leftExpression> extends never ? never :
Operation<rightExpression> | Integers<rightExpression> extends never ? never :
T
:never
type Expressions<T> = T extends string ? T extends Integers<T> ? T : T extends Operation<T> ? T : never : never
// Utility functions
type IfEquals<X, Y, A = unknown, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B;
type Undefined<T> = IfEquals<T, never, never, T> extends never ? Undefined<T> : T
// Public type validation genetic
type IsValidOrNever<T> = IfEquals<Expressions<T>, T, T, never>
type IsValidOrUndefined<T> = Undefined<IfEquals<Expressions<T>, T, T, never>>
// Tests
type TestNeverOkay = IsValidOrNever<'(1+2)*3'> // '(1+2)*3'
type TestNeverFail = IsValidOrNever<'1++2'> // never
type TestUndefinedOkay = IsValidOrUndefined<'(1+2)*3'> // '(1+2)*3'
type TestUndefinedFail = IsValidOrUndefined<'1++2'> // compile-time error "Type instantiation is excessively deep and possibly infinite"
有一些编译时验证,在当前示例中,它是一个简单的暴力算术表达式验证器,但可以是任意类型的表达式
目标是为公共通用类型提供导出,该类型接受带验证的自定义类型,并且最好用良好的类型执行一些类型计算,并为错误的类型抛出漂亮的编译时错误
不幸的是,只有两个众所周知的选项:返回类似 never 的类型 (IsValidOrNever),或者以 Haskell 风格调用发散函数,如 undefined (IsValidOrUndefined)
这些解决方案效果不错,但各有缺陷:IsValidOrNever 只是返回 never 类型,这可以在不会产生编译时错误的情况下使用;而 IsValidOrUndefined 会给客户发出奇怪的错误(类型实例化过深且可能无限)
问题:也许是否有第三种解决方案可以在这种情况下发出漂亮且可理解的编译时错误?类似于提到 IsValidOrX 的实例化将是适用的