从类型生成数组而不推断
Generate array from type without infering
我需要根据TS类型生成真正的JS代码,这样我就可以防止重复代码和造成混乱。
export interface TypesInfo {
mysqlDatabases: "db1" | "db2" | "db9";
}
//
export class Service {
protected static $SetInfo() {
return {
/*instead of true, it has to be TypesInfo.mysqlDatabases converted to array, so ["db1", "db2", "db9"];*/
mysql: true, // ["db1", "db2", "db9"] instead of true
mongodb: true
};
}
}
从数组推断类型对我不起作用,因为你不能将数组放在接口中,因为接口不是运行时的。
基本上 TypesInfo.mysqlDatabase
type 也应该作为启动器,而不仅仅是用于类型检查。
Infering the type from array won't work for me, because you can't put array inside interface, because interface isn't runtime.
您可以创建一个在运行时可用的枚举,而不是使用 mysqlDatabases: "db1" | "db2" | "db9";
。
export interface TypesInfo extends BaseServiceTypesInfo {
mysqlDatabases: YourTypeEnum ;
}
enum YourTypeEnum {
db1 = "db1",
db2 = "db2",
db9 = "db9",
}
...
mysql = Object.values(YourTypeEnum);
您希望避免指定数组值 mysql
及其元素类型 mysqlDatabases
的 union type 的冗余,因此您要求一种方法来生成来自联合类型的数组值。
不幸的是,这是不可能的;当 TypeScript 被编译为 JavaScript 时,TypesInfo
接口和所有接口一样,是 erased,因此没有任何东西可以用来构建你想要的数组。您无法从 TypeScript 类型生成 JavaScript 值。
不过你也可以用其他方式做事。所以这里避免冗余的唯一可能方法是从 JavaScript 值 ["db1", "db2", "db9"]
.
生成 TypeScript 类型 "db1" | "db2" | "db9"
如果您的 Service.$SetInfo()
静态方法是 public
而不是 protected
,那么最不冗余的方法是在 return 中显式定义数组值的 mysql
属性:
export class Service {
static $SetInfo() {
return {
mysql: ["db1", "db2", "db9"] as const,
mongodb: true
};
}
}
在这里(就像在所有此类解决方案中一样)我们使用其元素的 const
assertion to ask the compiler to remember the literal types。
然后,您可以根据 $SetInfo
方法定义 TypesInfo
的 mysqlDatabases
属性:
export interface TypesInfo {
mysqlDatabases: ReturnType<typeof Service.$SetInfo>['mysql'][number];
// (property) AllPublic.TypesInfo.mysqlDatabases: "db1" | "db2" | "db9"
}
这可行,但它 Service.$SetInfo
暴露给外部消费者。
如果您想将 Service.$SetInfo
保留为 protected
方法并且您不想公开任何其他内容,那么我认为唯一合理的方法是定义数组在你的模块中而不导出它,然后根据这个模块私有值定义 TypesInfo
和 $SetInfo
:
const mysqlDatabases = ["db1", "db2", "db9"] as const;
export interface TypesInfo {
mysqlDatabases: typeof mysqlDatabases[number];
}
export class Service {
protected static $SetInfo() {
return {
mysql: mysqlDatabases,
mongodb: true
};
}
}
这确实在您的模块中添加了另一个声明,并且您在评论中明确表示这是不可接受的。但我想展示它,因为它在包范围方面与您的原始代码非常相似。
您可以接受的中间立场是公开公开数组而不是整个 $SetInfo
方法,方法是使数组成为 Service
的单独静态 属性:
export class Service {
static mysqlDatabases = ["db1", "db2", "db9"] as const;
protected static $SetInfo() {
return {
mysql: Service.mysqlDatabases,
mongodb: true
};
}
}
export interface TypesInfo {
mysqlDatabases: typeof Service.mysqlDatabases[number];
}
我个人更喜欢三声明解决方案,因为似乎没有充分的理由将 mySqlDatabases
数组暴露给 Service
的外部用户,但如果你真的很看重只有两个模块中的声明,那么这可能是一个很好的折衷方案。
我需要根据TS类型生成真正的JS代码,这样我就可以防止重复代码和造成混乱。
export interface TypesInfo {
mysqlDatabases: "db1" | "db2" | "db9";
}
//
export class Service {
protected static $SetInfo() {
return {
/*instead of true, it has to be TypesInfo.mysqlDatabases converted to array, so ["db1", "db2", "db9"];*/
mysql: true, // ["db1", "db2", "db9"] instead of true
mongodb: true
};
}
}
从数组推断类型对我不起作用,因为你不能将数组放在接口中,因为接口不是运行时的。
基本上 TypesInfo.mysqlDatabase
type 也应该作为启动器,而不仅仅是用于类型检查。
Infering the type from array won't work for me, because you can't put array inside interface, because interface isn't runtime.
您可以创建一个在运行时可用的枚举,而不是使用 mysqlDatabases: "db1" | "db2" | "db9";
。
export interface TypesInfo extends BaseServiceTypesInfo {
mysqlDatabases: YourTypeEnum ;
}
enum YourTypeEnum {
db1 = "db1",
db2 = "db2",
db9 = "db9",
}
...
mysql = Object.values(YourTypeEnum);
您希望避免指定数组值 mysql
及其元素类型 mysqlDatabases
的 union type 的冗余,因此您要求一种方法来生成来自联合类型的数组值。
不幸的是,这是不可能的;当 TypeScript 被编译为 JavaScript 时,TypesInfo
接口和所有接口一样,是 erased,因此没有任何东西可以用来构建你想要的数组。您无法从 TypeScript 类型生成 JavaScript 值。
不过你也可以用其他方式做事。所以这里避免冗余的唯一可能方法是从 JavaScript 值 ["db1", "db2", "db9"]
.
"db1" | "db2" | "db9"
如果您的 Service.$SetInfo()
静态方法是 public
而不是 protected
,那么最不冗余的方法是在 return 中显式定义数组值的 mysql
属性:
export class Service {
static $SetInfo() {
return {
mysql: ["db1", "db2", "db9"] as const,
mongodb: true
};
}
}
在这里(就像在所有此类解决方案中一样)我们使用其元素的 const
assertion to ask the compiler to remember the literal types。
然后,您可以根据 $SetInfo
方法定义 TypesInfo
的 mysqlDatabases
属性:
export interface TypesInfo {
mysqlDatabases: ReturnType<typeof Service.$SetInfo>['mysql'][number];
// (property) AllPublic.TypesInfo.mysqlDatabases: "db1" | "db2" | "db9"
}
这可行,但它 Service.$SetInfo
暴露给外部消费者。
如果您想将 Service.$SetInfo
保留为 protected
方法并且您不想公开任何其他内容,那么我认为唯一合理的方法是定义数组在你的模块中而不导出它,然后根据这个模块私有值定义 TypesInfo
和 $SetInfo
:
const mysqlDatabases = ["db1", "db2", "db9"] as const;
export interface TypesInfo {
mysqlDatabases: typeof mysqlDatabases[number];
}
export class Service {
protected static $SetInfo() {
return {
mysql: mysqlDatabases,
mongodb: true
};
}
}
这确实在您的模块中添加了另一个声明,并且您在评论中明确表示这是不可接受的。但我想展示它,因为它在包范围方面与您的原始代码非常相似。
您可以接受的中间立场是公开公开数组而不是整个 $SetInfo
方法,方法是使数组成为 Service
的单独静态 属性:
export class Service {
static mysqlDatabases = ["db1", "db2", "db9"] as const;
protected static $SetInfo() {
return {
mysql: Service.mysqlDatabases,
mongodb: true
};
}
}
export interface TypesInfo {
mysqlDatabases: typeof Service.mysqlDatabases[number];
}
我个人更喜欢三声明解决方案,因为似乎没有充分的理由将 mySqlDatabases
数组暴露给 Service
的外部用户,但如果你真的很看重只有两个模块中的声明,那么这可能是一个很好的折衷方案。