编译 TypeScript 类型以供运行时使用
Compile TypeScript Types for runtime use
我有一个使用 REST 调用进行通信的客户端服务器应用程序。
为了防止我意外使用错误的类型,我在一个公共文件中定义了所有 RestCalls(摘录):
type def<TConnection extends Connections> =
// Authentication
TConnection extends '/auth/password/check/:login->get' ? Set<void, { found: boolean }, void, false>
: TConnection extends '/auth/password/register->post' ? Set<RegsiterAccount<Login>, void, void, false>
: TConnection extends '/auth/password/login->post' ? Set<Login, void, void, false>
: TConnection extends '/auth/webauth/challenge->get' ? Set<void, {
challenge: string,
id: string
}, void, false>
: TConnection extends '/auth/webauth/register->post' ? Set<RegsiterAccount<WebAuthN> & { comment: string }, void, void, false>
: TConnection extends '/auth/webauth/login->post' ? Set<Assertion, void, void, false>
: TConnection extends '/auth/logout->post' ? Set<void, void, void>
: TConnection extends '/auth/invite->get' ? Set<void, {
link: string,
validUntill: string
}, void>
: TConnection extends '/auth/invite/validate->post' ? Set<{ invite: string }, {
granted_by: string,
validUntill: string
}, void, false>
: TConnection extends '/auth/isAuthenticated->get' ? Set<void, {
isAuthenticated: boolean,
userName: string | undefined
}, void, false>
// default
: never
url和方法在字符串中编码,它也使用express url参数(/:
)。
Set
定义正文中的数据以及服务器是否应检查身份验证
- 要求
- 回应
- 错误响应
- 如果需要身份验证
type Set<Input extends (Object | void), result extends (Object | void), Error extends string | object | void, NeedsAuthentication extends boolean = true> = {
input: Input, result: result,
error: DefaultError<Error>,
authenticated: NeedsAuthentication
};
然后我可以使用以下类型来获取正确的值
export type InputBody<TPath extends Connections> = def<TPath>['input'];
export type InputPath<TPath extends Connections> = express.RouteParameters<TPath>;
export type Input<TPath extends Connections> = InputBody<TPath> & InputPath<TPath>;
export type Result<TPath extends Connections> = def<TPath>['result']
export type NeedsAuthentication<TPath extends Connections> = def<TPath>['authenticated']
export type Error<TPath extends Connections> = def<TPath>['error']
export type Method<T extends string> = T extends `${infer path}->${infer method}`
? method extends METHODS ? method
: never
: never;
export type Path<T extends string> = T extends `${infer path}->${infer method}`
? method extends METHODS ? path
: never
: never;
我现在想知道在运行时是否需要对特定呼叫进行身份验证。
我可以像对方法所做的那样在 url 中对其进行编码。或者使用 NeedsAuthentication
作为参数,其中还提供了 url。这样,当我输入 URL 时,我会自动完成,因为只有在需要身份验证时才会有 true,否则为 false。
我不喜欢这两种解决方法。
我想做的是
const needsAuthentication :boolean = NeedsAuthentication<'/auth/webauth/login->post'>;
有什么方法可以告诉编译器编译输出 JS 中的类型,这样我就可以在干扰参数需要时做同样的事情吗?
我目前唯一的其他解决方案是编写一个脚本,该脚本在构建前执行,它解析我的定义并为每个 URL 提供一个映射,如果它需要使用一些正则表达式解析进行身份验证...
编辑
我想实现以下功能
function needsAuthentication<T extends Connections>(test:T):NeedsAuthentication<T> {
// todo find out if authentication is actual required for this url
}
这不是传输数据的一部分,而是编码在类型映射中。
编译器会很好地将其映射为 true 或 false
对于 compieltime 上的 const 字符串(仍然不会发出实际值......)
我可以尝试使用打字稿编译器并调用任何计算 return 值的函数...
没有。 Typescript 类型将在编译阶段发出。
您的选择是:
- 使用 JSON-Schema (Ajv) 验证传入 http json 请求的输入:https://github.com/ajv-validator/ajv
- 使用 Swagger(与 (1) 几乎相同)。
- 使用适用于您的框架的验证器。
- 我发现这个项目试图从打字稿类型创建运行时断言:https://github.com/skunkteam/types。但是我自己从来没用过。
- https://github.com/nanoporetech/ts-runtime-typecheck - 与 (4) 相同,但也从未使用过。
我创建了一个运行预构建的脚本,并使用 TypeScript 编译器类型检查器为每个可能的输入推断函数的 retunrtype。
然后将其发送到一个文件,然后由该函数使用。
脚本
const project = new Project({});
// add source files
project.addSourceFilesAtPaths("src/**/*.ts");
function Test(path: string) {
const dataFile = project.createSourceFile(`src/${randomUUID()}.ts`, `import * as x from "./data" ; const check = x.needsAuthentication("${path}")`);
const declaraiton = dataFile.getVariableDeclarationOrThrow('check');
const result = project.getTypeChecker().getTypeText(declaraiton.getType());
return result.toLowerCase() == 'true';
}
const pathChecks = paths.map(x => [x, Test(x)]).reduce((p: any, v: any) => {
p[v[0]] = v[1];
return p;
}, {});
let authenticationText = `export const lookup = ${JSON.stringify(pathChecks)} as const;`
await fs.writeFile('src/data-authentication.g.ts', authenticationText);
在我的代码中我使用
import { lookup } from './data-authentication.g';
//...
export function needsAuthentication<T extends Connections>(test: T): NeedsAuthentication<T> {
return lookup[test];
}
这不是最快的方法,但确实有效...
我有一个使用 REST 调用进行通信的客户端服务器应用程序。
为了防止我意外使用错误的类型,我在一个公共文件中定义了所有 RestCalls(摘录):
type def<TConnection extends Connections> =
// Authentication
TConnection extends '/auth/password/check/:login->get' ? Set<void, { found: boolean }, void, false>
: TConnection extends '/auth/password/register->post' ? Set<RegsiterAccount<Login>, void, void, false>
: TConnection extends '/auth/password/login->post' ? Set<Login, void, void, false>
: TConnection extends '/auth/webauth/challenge->get' ? Set<void, {
challenge: string,
id: string
}, void, false>
: TConnection extends '/auth/webauth/register->post' ? Set<RegsiterAccount<WebAuthN> & { comment: string }, void, void, false>
: TConnection extends '/auth/webauth/login->post' ? Set<Assertion, void, void, false>
: TConnection extends '/auth/logout->post' ? Set<void, void, void>
: TConnection extends '/auth/invite->get' ? Set<void, {
link: string,
validUntill: string
}, void>
: TConnection extends '/auth/invite/validate->post' ? Set<{ invite: string }, {
granted_by: string,
validUntill: string
}, void, false>
: TConnection extends '/auth/isAuthenticated->get' ? Set<void, {
isAuthenticated: boolean,
userName: string | undefined
}, void, false>
// default
: never
url和方法在字符串中编码,它也使用express url参数(/:
)。
Set
定义正文中的数据以及服务器是否应检查身份验证
- 要求
- 回应
- 错误响应
- 如果需要身份验证
type Set<Input extends (Object | void), result extends (Object | void), Error extends string | object | void, NeedsAuthentication extends boolean = true> = {
input: Input, result: result,
error: DefaultError<Error>,
authenticated: NeedsAuthentication
};
然后我可以使用以下类型来获取正确的值
export type InputBody<TPath extends Connections> = def<TPath>['input'];
export type InputPath<TPath extends Connections> = express.RouteParameters<TPath>;
export type Input<TPath extends Connections> = InputBody<TPath> & InputPath<TPath>;
export type Result<TPath extends Connections> = def<TPath>['result']
export type NeedsAuthentication<TPath extends Connections> = def<TPath>['authenticated']
export type Error<TPath extends Connections> = def<TPath>['error']
export type Method<T extends string> = T extends `${infer path}->${infer method}`
? method extends METHODS ? method
: never
: never;
export type Path<T extends string> = T extends `${infer path}->${infer method}`
? method extends METHODS ? path
: never
: never;
我现在想知道在运行时是否需要对特定呼叫进行身份验证。
我可以像对方法所做的那样在 url 中对其进行编码。或者使用 NeedsAuthentication
作为参数,其中还提供了 url。这样,当我输入 URL 时,我会自动完成,因为只有在需要身份验证时才会有 true,否则为 false。
我不喜欢这两种解决方法。
我想做的是
const needsAuthentication :boolean = NeedsAuthentication<'/auth/webauth/login->post'>;
有什么方法可以告诉编译器编译输出 JS 中的类型,这样我就可以在干扰参数需要时做同样的事情吗?
我目前唯一的其他解决方案是编写一个脚本,该脚本在构建前执行,它解析我的定义并为每个 URL 提供一个映射,如果它需要使用一些正则表达式解析进行身份验证...
编辑
我想实现以下功能
function needsAuthentication<T extends Connections>(test:T):NeedsAuthentication<T> {
// todo find out if authentication is actual required for this url
}
这不是传输数据的一部分,而是编码在类型映射中。
编译器会很好地将其映射为 true 或 false
我可以尝试使用打字稿编译器并调用任何计算 return 值的函数...
没有。 Typescript 类型将在编译阶段发出。
您的选择是:
- 使用 JSON-Schema (Ajv) 验证传入 http json 请求的输入:https://github.com/ajv-validator/ajv
- 使用 Swagger(与 (1) 几乎相同)。
- 使用适用于您的框架的验证器。
- 我发现这个项目试图从打字稿类型创建运行时断言:https://github.com/skunkteam/types。但是我自己从来没用过。
- https://github.com/nanoporetech/ts-runtime-typecheck - 与 (4) 相同,但也从未使用过。
我创建了一个运行预构建的脚本,并使用 TypeScript 编译器类型检查器为每个可能的输入推断函数的 retunrtype。 然后将其发送到一个文件,然后由该函数使用。
脚本
const project = new Project({});
// add source files
project.addSourceFilesAtPaths("src/**/*.ts");
function Test(path: string) {
const dataFile = project.createSourceFile(`src/${randomUUID()}.ts`, `import * as x from "./data" ; const check = x.needsAuthentication("${path}")`);
const declaraiton = dataFile.getVariableDeclarationOrThrow('check');
const result = project.getTypeChecker().getTypeText(declaraiton.getType());
return result.toLowerCase() == 'true';
}
const pathChecks = paths.map(x => [x, Test(x)]).reduce((p: any, v: any) => {
p[v[0]] = v[1];
return p;
}, {});
let authenticationText = `export const lookup = ${JSON.stringify(pathChecks)} as const;`
await fs.writeFile('src/data-authentication.g.ts', authenticationText);
在我的代码中我使用
import { lookup } from './data-authentication.g';
//...
export function needsAuthentication<T extends Connections>(test: T): NeedsAuthentication<T> {
return lookup[test];
}
这不是最快的方法,但确实有效...