typescript - 在 TypeScript 中,我们可以使用“运行时”键来定义新类型吗?
问题描述
更好地解释这个作为一个例子:
class ModuleOptions {
key1?: string;
key2?: string;
keyA?: string;
keyB?: string;
}
class Module {
static options: ModuleOptions = {
key1: 'key1',
key2: 'key2',
keyA: 'keyA',
keyB: 'keyB'
};
static create(options: ModuleOptions) {
Object.assign(Module.options, options);
}
}
const myModule = Module.create({
key1: 'foo'
});
// Now Module.options static field has "foo" as key1...
// Could we constrait fooArgs to have a property named "foo" of type string?
function foo(fooArgs: NewType) {
// Do stuff
}
是否可以fooArgs
(即NewType
)只接受'foo'
作为键,并为其定义相同的类型(string
)?
这不起作用(即使变得简单):
class NewType {
[k in keyof [ModuleOptions.options]]: string;
}
计算属性名称必须是“字符串”、“数字”、“符号”或“任意”类型。
解决方案
你可能会得到你想要的东西,但肯定有痛点。第一个主要问题是 TypeScript 不允许您任意改变现有值的类型。因此,如果名为的变量foo
具有 type string
,则不能将其更改为number
以后。在您的情况下,如果Module.options.key1
编译器知道它是string-literal type "key1"
,那么您以后不能将其类型更改为 string-literal 类型"foo"
。编译器会期望它"key1"
永远存在,即使在您调用Module.create()
. 有几种方法可以解决这个问题。一种是使用类型保护来缩小范围值的类型(值的类型可以更具体,但不能随意改变),还有一种是用新的变量来表示新类型的值。
这是使用后一种方法的可能解决方案...返回具有不同类型create
的新版本:Module
module Module {
type Module = typeof Module; // handy type alias
const defaultOptions = {
key1: 'key1' as 'key1',
key2: 'key2' as 'key2',
keyA: 'keyA' as 'keyA',
keyB: 'keyB' as 'keyB',
}; // use type assertions to narrow to string literals
type DefaultOptions = typeof defaultOptions; // handy type alias
export const options: Record<keyof DefaultOptions, string> = defaultOptions;
// represent overwriting properties from T with properties from U
type Assign<T, U> = {
[K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never
};
export function create<T>(
o: T
): { [K in keyof Module]: 'options' extends K ? Assign<DefaultOptions, T> : Module[K] } {
Object.assign(options, o);
return Module as any; // can't represent type mutation, use type assertion here
}
}
它使用条件类型来表示options
调用create
. 让我们看看它是如何工作的:
const almostMyModule = Module.create({
key1: 'foo'
});
almostMyModule.options.key2; // "key2"
almostMyModule.options.key1; // string? oops
糟糕,传递给的参数的类型create()
被推断为{key1: string}
,而不是{key1: "foo"}
。这是另一个痛点。有一些方法可以使推理正确,但现在我只使用类型断言来缩小它:
const myModule = Module.create({
key1: 'foo' as 'foo'
});
myModule.options.key1; // 'foo', that's better
myModule.options.key2; // 'key2'
现在myModule.options.key1
编译器知道它是"foo"
,我们可以制作NewType
你想要的:
type ValueOf<T> = T[keyof T];
type NewType = { [K in ValueOf<typeof myModule.options>]: string };
检查NewType
给我们:
// type NewType = {
// foo: string;
// key2: string;
// keyA: string;
// keyB: string;
// }
你可以使用它:
function foo(fooArgs: NewType) {
fooArgs.foo // okay
}
希望有帮助。祝你好运!
推荐阅读
- image - svg:image 元素并不总是被渲染/下载
- sublimetext3 - Sublime Text 语法将 html 文件高亮为 babel/javascript
- parallel-processing - STM32L4 //基于中断的并行SPI
- sql - 通过@timestamp 查询 ElasticSearch SQL 过滤
- c++ - 隐式移动构造函数和赋值运算符
- firebase - 在 FLutter 中返回 null 的函数
- wordpress - Wordpress 显示 single.php 两次
- ios - 根据百分比划分两个视图的空间
- python - 如何在 Pandas 中初始化数据框时从文件中读取满足某些条件的特定行和列?
- amazon-web-services - 具有特定 Fargate 平台版本的 ECS 计划任务(在 CDK 上)