Javascript 以字符串数组的形式获取Typescript接口的键
我在中有很多表以及它们各自的接口,用于它们所具有的列 例如:Javascript 以字符串数组的形式获取Typescript接口的键,javascript,typescript,Javascript,Typescript,我在中有很多表以及它们各自的接口,用于它们所具有的列 例如: 导出接口IMyTable{ id:编号; 标题:字符串; createdAt:日期; isDeleted:布尔值; } 我希望将此接口的属性名称放在如下数组中: export class IMyTable { constructor( public id = '', public title = '', public createdAt: Date = null,
导出接口IMyTable{
id:编号;
标题:字符串;
createdAt:日期;
isDeleted:布尔值;
}
我希望将此接口的属性名称放在如下数组中:
export class IMyTable {
constructor(
public id = '',
public title = '',
public createdAt: Date = null,
public isDeleted = false
)
}
const IMyTable=[“id”、“title”、“createdAt”、“isDeleted”];
我不能直接基于接口IMyTable
创建一个对象/数组,因为这样会动态获取表的接口名称。因此,我需要在接口中迭代这些属性,并从中获取一个数组
我怎样才能达到这个结果?不行。接口在运行时不存在 变通办法
创建该类型的变量并在其上使用
Object.keys
您需要创建一个实现接口的类,实例化它,然后使用Object.keys(您的对象)
来获取属性
export class YourClass implements IMyTable {
...
}
然后
从TypeScript2.3开始(或者我应该说是2.4,就像在2.3中一样),此功能包含在typescript@2.4-dev),您可以创建一个自定义转换器来实现您想要做的事情
实际上,我已经创建了这样一个自定义转换器,它支持以下功能
从'ts transformer key'导入{keys};
界面道具{
id:字符串;
名称:字符串;
年龄:人数;
}
常量keysOfProps=keys();
console.log(按键操作);//['id'、'姓名'、'年龄']
不幸的是,定制变压器目前并不容易使用。您必须将它们与TypeScript转换API一起使用,而不是执行tsc命令。正在请求对自定义转换器的插件支持。请尝试将其定义为类,而不是像在接口中那样定义
IMyTable
。在typescript中,您可以使用类似接口的类
因此,对于您的示例,定义/生成您的类,如下所示:
export class IMyTable {
constructor(
public id = '',
public title = '',
public createdAt: Date = null,
public isDeleted = false
)
}
将其用作接口:
export class SomeTable implements IMyTable {
...
}
获取密钥:
const keys = Object.keys(new IMyTable());
以下要求您自己列出键,但至少TypeScript将强制执行
IUserProfile
和IUserProfileKeys
具有完全相同的键():
导出接口IUserProfile{
id:字符串;
名称:字符串;
};
键入KeysEnum={[P in keyof Required]:true};
常量IUserProfileKeys:KeysEnum={
id:是的,
姓名:对,
};
这应该行得通
var IMyTable: Array<keyof IMyTable> = ["id", "title", "createdAt", "isDeleted"];
我面临着一个类似的问题:我有一个巨大的属性列表,我希望它既是一个接口(编译时),又是一个对象(运行时) 注意:我不想写(用键盘键入)两次属性浓>干
这里需要注意的一点是,接口在编译时是强制类型,而对象大多是运行时类型。() 如中所述,@derek,接口和对象的公分母可以是同时服务于类型和值的类 因此,TL;DR,以下代码应满足需要:
类MyTableClass{
//在这里列出属性,只写一次
id=“”;
title=“”;
isDeleted=假;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//这是要使用/导出的纯接口版本
接口IMyTable扩展MyTableClass{};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//道具类型为要导出的数组
键入MyTablePropsArray=Array;
//道具阵列本身!
常量propsArray:MyTablePropsArray=
key(新的MyTableClass())作为MyTablePropsArray;
console.log(propsArray);//打印出[“id”、“标题”、“isDeleted”]
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//创建纯实例作为对象的示例
const tableInstance:MyTableClass={//工作正常!
id:“3”,
标题:“你好”,
isDeleted:错,
};
(以上代码是否在Typescript游乐场中玩得更多)
另外,如果您不想为类中的属性分配初始值,并且继续使用该类型,则可以使用构造函数技巧:
class MyTableClass {
// list the propeties here, ONLY WRITTEN ONCE
constructor(
readonly id?: string,
readonly title?: string,
readonly isDeleted?: boolean,
) {}
}
console.log(Object.keys(new MyTableClass())); // prints out ["id", "title", "isDeleted"]
.安全型
从具有安全编译时检查的接口创建一个数组或一组键需要一些创造性。类型在运行时被擦除,对象类型(无序、命名)无法转换为元组类型(有序、未命名)
与其他答案的比较
这里提出的变体都考虑/触发一个编译错误,如果给定一个引用对象类型,如IMyTable
,则会出现重复或丢失的元组项。例如,声明(keyof IMyTable)[
的数组类型无法捕获这些错误
另外,它们不需要(最后的变体使用代码> TS变形/代码>,我认为这是一个通用编译器包装器),发出元组类型(只有第一个解决方案创建一个数组)或宽数组类型(比较)和最后。 变量1:简单类型数组
+
tuple+-
手动,自动完成+-
更高级、更复杂的类型
解释
createKeys
编译时是否使用附加的断言类型检查函数参数类型,这些类型会因输入不合适而发出错误(keyof IMyTable)[]|[keyof IMyTable]
是一种强制从被叫方推断元组而不是数组的方法。或者,您可以从调用方使用
CheckMissing
检查,如果T
错过了U
中的键:
type CheckMissing<T extends readonly any[], U extends Record<string, any>> = {
[K in keyof U]: K extends T[number] ? never : K
}[keyof U] extends never ? T : T & "Error: missing keys"
type T1 = CheckMissing<["p1"], {p1:any, p2:any}> //["p1"] & "Error: missing keys"
type T2 = CheckMissing<["p1", "p2"], { p1: any, p2: any }> // ["p1", "p2"]
注意:有关元组中唯一项检查的更多信息,请参阅。使用,我们还可以命名错误字符串中缺少的键-请查看
变量3:递归类型 在版本4.1中,TypeScript正式支持,这里也可以使用它。尽管如此,由于组合复杂性,类型计算是昂贵的——超过5-6项的性能会大幅下降。为了完整性,我列出了这个备选方案(): 我们合作之后
var IMyTable: Array<keyof IMyTable> = ["id", "title", "createdAt", "isDeleted"];
var IMyTable: (keyof IMyTable)[] = ["id", "title", "createdAt", "isDeleted"];
class MyTableClass {
// list the propeties here, ONLY WRITTEN ONCE
constructor(
readonly id?: string,
readonly title?: string,
readonly isDeleted?: boolean,
) {}
}
console.log(Object.keys(new MyTableClass())); // prints out ["id", "title", "isDeleted"]
// Record type ensures, we have no double or missing keys, values can be neglected
function createKeys(keyRecord: Record<keyof IMyTable, any>): (keyof IMyTable)[] {
return Object.keys(keyRecord) as any
}
const keys = createKeys({ isDeleted: 1, createdAt: 1, title: 1, id: 1 })
// const keys: ("id" | "title" | "createdAt" | "isDeleted")[]
function createKeys<T extends readonly (keyof IMyTable)[] | [keyof IMyTable]>(
t: T & CheckMissing<T, IMyTable> & CheckDuplicate<T>): T {
return t
}
type CheckMissing<T extends readonly any[], U extends Record<string, any>> = {
[K in keyof U]: K extends T[number] ? never : K
}[keyof U] extends never ? T : T & "Error: missing keys"
type T1 = CheckMissing<["p1"], {p1:any, p2:any}> //["p1"] & "Error: missing keys"
type T2 = CheckMissing<["p1", "p2"], { p1: any, p2: any }> // ["p1", "p2"]
type CheckDuplicate<T extends readonly any[]> = {
[P1 in keyof T]: "_flag_" extends
{ [P2 in keyof T]: P2 extends P1 ? never :
T[P2] extends T[P1] ? "_flag_" : never }[keyof T] ?
[T[P1], "Error: duplicate"] : T[P1]
}
type T3 = CheckDuplicate<[1, 2, 3]> // [1, 2, 3]
type T4 = CheckDuplicate<[1, 2, 1]>
// [[1, "Error: duplicate"], 2, [1, "Error: duplicate"]]
type Prepend<T, U extends any[]> = [T, ...U] // TS 4.0 variadic tuples
type Keys<T extends Record<string, any>> = Keys_<T, []>
type Keys_<T extends Record<string, any>, U extends PropertyKey[]> =
{
[P in keyof T]: {} extends Omit<T, P> ? [P] : Prepend<P, Keys_<Omit<T, P>, U>>
}[keyof T]
const t1: Keys<IMyTable> = ["createdAt", "isDeleted", "id", "title"] // ✔
// ./src/mybuildstep.ts
import {Project, VariableDeclarationKind, InterfaceDeclaration } from "ts-morph";
const project = new Project();
// source file with IMyTable interface
const sourceFile = project.addSourceFileAtPath("./src/IMyTable.ts");
// target file to write the keys string array to
const destFile = project.createSourceFile("./src/generated/IMyTable-keys.ts", "", {
overwrite: true // overwrite if exists
});
function createKeys(node: InterfaceDeclaration) {
const allKeys = node.getProperties().map(p => p.getName());
destFile.addVariableStatement({
declarationKind: VariableDeclarationKind.Const,
declarations: [{
name: "keys",
initializer: writer =>
writer.write(`${JSON.stringify(allKeys)} as const`)
}]
});
}
createKeys(sourceFile.getInterface("IMyTable")!);
destFile.saveSync(); // flush all changes and write to disk
// ./src/generated/IMyTable-keys.ts
const keys = ["id","title","createdAt","isDeleted"] as const;
npm install --save-dev ts-morph
import {
Project,
VariableDeclarationKind,
InterfaceDeclaration,
} from "ts-morph";
// initName is name of the interface file below the root, ./src is considered the root
const Keys = (intName: string): string[] => {
const project = new Project();
const sourceFile = project.addSourceFileAtPath(`./src/${intName}.ts`);
const node = sourceFile.getInterface(intName)!;
const allKeys = node.getProperties().map((p) => p.getName());
return allKeys;
};
export default Keys;
import keys from "./keys";
const myKeys = keys("MyInterface") //ts file name without extension
console.log(myKeys)