在库的核心中,打字是非常有用的,如果我们能轻松地使用它进行验证,它可能会更有用。
有一些库试图在这方面改进编程体验,但由于语言本身的限制而失败。
我离搜索主题越近,就越是使用 zod 库,然而我仍然需要为类型和内容分别提供信息。
生成数据的部分可以被认为是 Non-goal
- 在程序中添加或依赖运行时类型信息,或者根据类型系统的结果发出不同的代码。相反,鼓励不要求运行时元数据的编程模式。
从编译时间常量定义泛型类型更接近于消除似乎不必要的障碍,因为常量和类型接受相同的输入。
搜索词
指的是一个值,但用作类型
建议
我的建议是将编译时常量用作类型或类型的值。
目前可以以有趣的方式操作类型。
type JSONSchema = {type: string, items?: JSONSchema, properties?: {[key: string]: JSONSchema}}
type TypeFromSchema<T extends JSONSchema> =
T['type'] extends 'string' ? string:
T['type'] extends 'boolean' ? boolean:
T['type'] extends 'number'|'integer' ? number:
T['type'] extends 'array' ?
// ignore heterogeneous arrays
(T['items'] extends JSONSchema ? TypeFromSchema<T['items']>: never):
T['type'] extends 'object' ? {
// neglect the required fields
[k in keyof T['properties']]?: TypeFromSchema<Extract<T['properties'][k], JSONSchema>>
} : never;
type stringSchema = {"type": "string"};
type booleanSchema = {"type": "boolean"};
type numberSchema = {"type": "number"};
type objectSchema = {
"type": "object",
"properties": {
"boolean": {"type": "boolean"},
"integer": {"type": "integer"},
"number": {"type": "number", "maxLength": 20};
"string": {"type": "stirng", "required": true};
}
}
let validString: TypeFromSchema<stringSchema>; // string
let validNumber: TypeFromSchema<numberSchema>; // number
let validBoolean: TypeFromSchema<booleanSchema>; // boolean
// {boolean?: boolean, integer?: number, number?: number, string?: string}
let obj: TypeFromSchema<objectSchema> = {};
我想让 booleanSchema
作为 const
而不是 type
。或者能够提取要在运行时使用的类型的信息。
用例
这将使更有趣的数据验证成为可能,例如上面提到的 Zod 这样的库就可以仅使用类型声明来工作。
示例
我在这里给出一些可以被视为替代方案的例子
编译时常量到类型
这样我可以声明 Model
类型的变量,启用 IDE 辅助和编译时类型检查,在运行时我可以使用 model
值进行动态数据验证。
declare function Validate<T, U extends SchemaFromType<T>>(data, schema: U): T;
const X = Validate<Model>(fetchData(), model);
对象模型是可变的,所以如果我修改它,它就会变得不一致,这将表现为深拷贝,它只在执行该行时才考虑值。
类型到编译时常量(也许不是目标)
以下候选项将提供提取类型信息的机制
type Coord = {latitude: number, longitude: number, height?: number};
const CoordProperties = KeysOf<Coord>;
const CoordOptional = OptionalKeysOf<Coord>;
或者更通用的东西,如
const ScalarSchema<T extends string | boolean | number> {
[k keyof T]:
T[k] extends string ? {type: 'string'}:
T[k] extends boolean ? {type: 'boolean'} ?
T[k] extends number ? {type: 'number'} : never;
}
这些构造可以在许多代码中多次使用,以避免增加太多代码的大小。
融合赋值和类型声明(给关键字 type
带来新用途)
在以下候选项中,有一个赋值和精确类型的定义合并在一个指令中。例如 TypeOf(rightAngle) = {angle: number}
是 rightAngle
的一般化,另一方面 RightAngle = {angle: 90}
将是一个精确类型。同样的情况也适用于从 JSON 或其他模块导入数据。
const rightAngle: type RightAngleType = {angle: 90};
import modelSchema: type ModelSchemaType from 'complex-data-schema.json';
融合类型和模式定义(也许不是目标)
在以下候选项中,我们将定义类型 Color 和一个 schema,其中指定类型的写入。这可以用于任何广泛的模式,并在编译期间转换,可能需要 tsconfig.json
中指定的脚本(如果需要)。
type Color: schema colorSchema = 'red' | 'green' | 'blue';
然后 colorSchema
将被设置为
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "string",
"enum": ["read", "green", "blue"]
}
检查表
我的建议符合以下准则:
- 这不会对现有的 TypeScript/JavaScript 代码造成破坏性的变化
- 这不会改变现有 JavaScript 代码的运行时行为
- 这可以在不根据表达式的类型发射不同的 JS 的情况下实现
- 这不是一个运行时特性(例如库功能、非 ECMAScript 语法与 JavaScript 输出等)
- 这个特性(在其某些实现中)将同意 TypeScript's Design Goals 中的一些东西。
6条答案
按热度按时间dvtswwa31#
这部分问题
typeof
已经解决了吗?k75qkfdt2#
对于 typeof 的对象,它给出的是一个广义类型,而不是一个确切的类型。
标量示例给出的是确切的类型,下面的示例将失败,因为
red
不能分配给typeof color = 'blue'
:const blue = 'blue';
const red: typeof color = 'red';
但是在对象中,它会给出一个泛型类型,下一个示例可以编译,因为
typeof color = {name: string}
而不是{name: 'blue'}
:const color = {name: 'blue'};
const red: typeof color = {name: 'red'};
对于数组,下一个示例可以工作,因为
typeof collorArray = string[]
:const colorArray = ['red', 'green', 'blue'];
const newColors: typeof colorArray = ['black'];
但是还有其他类型,colorArray 可以分配给它们,我能想到的有
[string, string, string]
、('red' | 'green' | 'blue')[]
,最严格的是['red', 'green', 'blue']
,这是唯一一个可以通过使用类型转换派生其他类型的类型。我希望这些转换已经可用了。vsaztqbk3#
听起来你想要更频繁地使用
as const
ilmyapht4#
谢谢你指出这一点,我不知道这个功能。这肯定会帮助我从模式中获取一些东西到类型。
上面的示例
这将我的期望重置为实现从模式定义类型的目标,如果我实现了这个目标,我可以发布一个包,它可能会找到用途。但是编写TypeScript并生成模式肯定更容易,因为它更易读,不像模式那样冗长。你有什么建议可以帮助我这样做吗?
b4lqfgs45#
我有一些情况,其中
as const
无法完成任务。能够从单值泛型类型创建常量值将是非常棒的。它将允许利用单态性来执行动态检查。这个例子并不是“最优”的,因为它可以使用“最大键”来减少我们访问字段的次数,甚至更好的是,它可以使用具有相同结构的对象,但在叶子节点中有布尔值或空值。无论如何,它说明了这样一个想法:
klr1opcd6#
我明白你的观点@castarco,但我也理解这对项目来说是一个非目标。这将为每种类型生成不同的函数,而且在javascript中无法工作。我们可以使用初始提案来代替,让
validateRequired
接收由类型生成的信息作为参数。换句话说,type to constant
Map不能与泛型类型一起使用。感谢你的留言,谁知道这可能会成为未来的一个功能呢。