typescript - 基于动态(映射/条件)类型获取类型完成
问题描述
您可以将以下代码放入 foo.ts 文件中。我正在尝试动态生成类型。我正在做的是基于这个问题: 将数组映射到接口
type TypeMapping = {
Boolean: boolean,
String: string,
Number: number,
ArrayOfString: Array<string>,
}
export enum Type {
Boolean = 'Boolean',
String = 'String',
Number = 'Number',
ArrayOfString = 'ArrayOfString'
}
const asOptions = <K extends Array<string>, T extends Array<{ name: K, type: keyof TypeMapping }>>(t: T) => t;
type OptionsToType<T extends Array<{ name: Array<string>, type: keyof TypeMapping }>>
= { [K in T[number]['name'][0]]: TypeMapping[Extract<T[number], { name: K }>['type']] }
const options = asOptions([
{
name: ['foo'],
type: Type.Boolean
},
{
name: ['bar'],
type: Type.String
},
{
name: ['baz'],
type: Type.Number
},
{
name: ['bab'],
type: Type.ArrayOfString
}
]);
export type Opts = OptionsToType<typeof options>;
const v = <Opts>{foo: true}; // this does not compile
console.log(typeof v.foo);
我没有得到任何类型完成 - 当我输入v.
任何内容时。
解决方案
这是一个使用 Typescript 3 和一个对象作为输入的示例。我在自己的项目中做了与此非常相似的事情,以从我的 Postgres 数据库中为 knex.js 生成一个类型化的查询构建器包装器。
// for lazier enum/mapping declaration
function StrEnum<T extends string[]>(...values: T) {
let o = {};
for (let v in values) {
o[v] = v;
}
return o as { [K in T[number]]: K };
}
// declare enum values
const Type = StrEnum("Boolean", "String", "Number", "ArrayOfString");
// correlate the keys to things
type TypeMapping = {
Boolean: boolean;
String: string;
Number: number;
ArrayOfString: Array<string>;
};
// thing to convert your generated interface into something useful
const asOptions = <T extends { [key: string]: keyof TypeMapping }>(t: T) => t;
// the generated object
const options = asOptions({
foo: Type.Boolean,
bar: Type.String,
baz: Type.Number,
bab: Type.ArrayOfString
});
type Opts = Partial<
{ [V in keyof typeof options]: TypeMapping[typeof options[V]] }
>;
const v: Opts = { foo: true }; // this does compile
console.log(v);
这是使用当前界面的一种方法:
// for lazier enum/mapping declaration
function StrEnum<T extends string[]>(...values: T) {
let o = {};
for (let v in values) {
o[v] = v;
}
return o as { [K in T[number]]: K };
}
// declare enum values
const Type = StrEnum("Boolean", "String", "Number", "ArrayOfString");
// correlate the keys to things
type TypeMapping = {
Boolean: boolean;
String: string;
Number: number;
ArrayOfString: Array<string>;
};
type OptDefinitionElement<K extends string, V extends keyof TypeMapping> = {
name: K;
value: V;
};
// thing to convert your generated interface into something useful
const asOptions = <T extends OptDefinitionElement<any, any>[]>(...t: T) => {
return t;
};
// because typescript doesn't like to infer strings
// nested inside objects/arrays so precisely
function InferString<S extends string>(s: S) {
return s;
}
// the generated object
const options = asOptions(
{ name: InferString("foo"), value: Type.Boolean },
{ name: InferString("bar"), value: Type.String },
{ name: InferString("baz"), value: Type.Number },
{ name: "bab" as "bab", value: Type.ArrayOfString } // note you don't *have* to use the Infer helper
);
// way to iterate keys and construct objects, and then result in the | type of all
// of the values
type Values<T extends { [ignoreme: string]: any }> = T extends {
[ignoreme: string]: infer R;
}
? R
: never;
type OptionsType = typeof options;
type OptionKeys = Exclude<keyof OptionsType, keyof Array<any>>;
type Opts = Values<
{
[TupleIndex in Exclude<keyof OptionsType, keyof Array<any>>]: {
[key in OptionsType[TupleIndex]["name"]]: TypeMapping[OptionsType[TupleIndex]["value"]]
}
}
>;
const v: Opts = { foo: true }; // this does compile
console.log(v);
推荐阅读
- omnet++ - 如何从 omnet++ 中的 c++ 代码访问运行时 queueLength 参数?
- sql - 将长 SQL 代码放入 VBA Excel 的最简单方法是什么?
- java - 如何从 Mockito Junit 正确调用超类方法
- c# - 是否有可能知道在 IEnumerable 上调用了哪些操作?
- android - 如何在 Android 项目中为单元测试提供自定义应用程序类
- java - 当我按下按钮调用我的函数时,我的应用程序崩溃了
- geoserver - Geoserver 为图像金字塔寻找 qix 文件
- javascript - 如果语句在 p5.js 中不起作用。为什么是这样?
- amazon-web-services - 为什么与 Aurora Serverless 的连接的主机名来自 VPC 外部?
- vb.net - 将原始文件转换为 .jpg 文件 VB.net