更好地举例说明:
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;
}
计算出的属性名称必须为'string','number','symbol'或'any'类型。
答案 0 :(得分:2)
您可能会得到想要的东西,但是肯定有痛点。第一个主要问题是TypeScript不允许您随意更改现有值的类型。因此,如果名为foo
的变量具有类型string
,则以后不能将其更改为number
。在您的情况下,如果编译器将Module.options.key1
称为string-literal type "key1"
,那么以后就无法将其类型更改为字符串文字类型"foo"
。即使您调用"key1"
,编译器也希望它永远是Module.create()
。有几种解决方法。一种是使用type guards来缩小值的类型(值的类型可以更具体,但不能任意更改),另一种是使用新变量来表示新类型的值。
这是使用后一种方法的可能解决方案...让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
}
}
使用conditional types表示当您致电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
}
希望有帮助。祝你好运!