我有两组要彼此关联的类型。
// Group A
interface Hello { ... }
interface Foo { ... }
// Group B
interface World { ... }
interface Bar { ... }
为此,我正在构建第三个接口,该接口充当从一种类型到另一种类型的查找:
interface LookupMap {
Hello: World;
Foo: Bar;
}
最终,我希望能够使用一个接口作为LookupMap
的索引器从一种类型转换为另一种类型(类似于如何使用字符串和数字在索引的对象常量中查找类型):
type AltType<T> = LookupMap<T>;
const altHello: AltType<Hello> = ...; // should be of type World
const altFoo: AltType<Foo> = ...; // should be of type Bar
这不起作用-似乎不能将类型用作此类的索引器。
我正在尝试为Immutable.js添加一些更好的类型。
此刻下面有一些粗糙的代码。如果您已经找到了解决方案,那么不用阅读所有这些内容就可以了……
不可变对象上有很多有用的功能,为方便起见,让我们尝试为Map.get
添加类型。
只要您的所有值都是原始值,实际上就很简单:
interface Immutalizer<MUTABLE_TYPE> extends Immutable.Map<keyof MUTABLE_TYPE, MUTABLE_TYPE[keyof MUTABLE_TYPE]> {
get<PROP_NAME extends keyof MUTABLE_TYPE>(prop: PROP_NAME, notSetValue?: MUTABLE_TYPE[PROP_NAME]): MUTABLE_TYPE[PROP_NAME];
}
interface MyInterface {
hello: boolean;
world: string;
foo: number;
bar: symbol;
}
interface MyImmutableInterface extends Immutalizer<MyInterface> {};
const myObject: MyImmutableInterface = Immutable.fromJS(...);
myObject.get("hello"); // boolean
myObject.get("world"); // string
myObject.get("foo"); // number
myObject.get("bar"); // symbol
深入探讨一下,如果我们的某些道具是复杂的对象,我们必须为Immutalizer
提供第二种类型以提供一些上下文:
interface Immutalizer<
MUTABLE_TYPE,
COMPLEX_OBJECT_KEYMAP extends { [PROP_NAME in keyof MUTABLE_TYPE]: any } = MUTABLE_TYPE
> extends Immutable.Map<
keyof MUTABLE_TYPE,
COMPLEX_OBJECT_KEYMAP[keyof MUTABLE_TYPE]
> {
get<PROP_NAME extends keyof MUTABLE_TYPE>(prop: PROP_NAME, notSetValue?: COMPLEX_OBJECT_KEYMAP[PROP_NAME]): COMPLEX_OBJECT_KEYMAP[PROP_NAME];
}
interface Hello {
foo: string;
bar: World;
}
interface World {
a: number;
b: symbol;
}
interface ImmutableHello extends Immutalizer<Hello, {
foo: string;
bar: ImmutableWorld;
}> {};
interface ImmutableWorld extends Immutalizer<World> {}; // this one is all primitives so the default type will cover it
const myObject: ImmutableHello = Immutable.fromJS(...);
myObject.get("bar"); // ImmutableWorld
myObject.get("foo"); // string
myObject.get("bar").get("a"); // number
myObject.get("bar").get("b"); // symbol
这是大量的工作,而且只会随着对象树的深入而变得更糟-因此,我制定了一个替代解决方案,该解决方案距离工作范围还很近,但并不完全在那里:
type Primitive = string | number | boolean | symbol | String | Number | Boolean | Symbol;
type PrimitiveSwitch<PROP_TYPE, IMMUTALIZER_MAP extends ImmutalizerMap> =
PROP_TYPE extends Primitive ?
PROP_TYPE :
IMMUTALIZER_MAP[PROP_TYPE]; // TYPE ERROR: Type 'PROP_TYPE' cannot be used to index type 'IMMUTALIZER_MAP'.
interface ImmutalizerMap { [mutableType: string]: Immutalizer<any, this> }
interface Immutalizer<MUTABLE_TYPE, IMMUTALIZER_MAP extends ImmutalizerMap> {
get<PROP_NAME extends keyof MUTABLE_TYPE>(
prop: PROP_NAME,
notSetValue?: PrimitiveSwitch<MUTABLE_TYPE[PROP_NAME], IMMUTALIZER_MAP>
): PrimitiveSwitch<MUTABLE_TYPE[PROP_NAME], IMMUTALIZER_MAP>;
}
export interface Hello {
foo: string;
bar: World;
}
export interface World {
a: number;
b: symbol;
}
interface ImmutableHello extends Immutalizer<Hello, ImmutalizerMap> { }
interface ImmutableWorld extends Immutalizer<World, ImmutalizerMap> { }
interface MyImmutalizerMap {
Hello: ImmutableHello;
World: ImmutableWorld;
}
const hello: ImmutableHello = Immutable.fromJS(...);
hello.get("foo"); // string
hello.get("bar"); // unknown (should be ImmutableWorld)
Immutalizer
本身本身有点难读,但是使用它现在(理论上)很容易:
Immutalizer
通过传递基本类型和它所属的ImmutalizerMap
来形成不可变类型Immutalizer
完成其余工作,使用PrimitiveSwitch
确定是否需要在ImmutalizerMap
中查找任何给定类型。但是,如以上抽象版本所述,在IMMUTALIZER_MAP[PROP_TYPE]
中访问PrimitiveSwitch
会引发类型错误:Type 'PROP_TYPE' cannot be used to index type 'IMMUTALIZER_MAP'.
接口(名称)可以用作其他接口的索引器吗?对不可变类型是否有更好的解决方案?
答案 0 :(得分:0)
不是索引器问题的答案,我确实找到了使用递归将类型添加到Immutable.js的更完整解决方案:
type Primitive = string | number | boolean | symbol | String | Number | Boolean | Symbol;
type PrimitiveSwitch<MUTABLE_TYPE, PROP_NAME extends keyof MUTABLE_TYPE> = MUTABLE_TYPE[PROP_NAME] extends Primitive ? MUTABLE_TYPE[PROP_NAME] : Immutalizer<MUTABLE_TYPE[PROP_NAME]>
interface Immutalizer<MUTABLE_TYPE> extends Immutable.Map<keyof MUTABLE_TYPE, Immutalizer<MUTABLE_TYPE[keyof MUTABLE_TYPE]> | MUTABLE_TYPE[keyof MUTABLE_TYPE]> {
get<PROP_NAME extends keyof MUTABLE_TYPE>(prop: PROP_NAME, notSetValue?: PrimitiveSwitch<MUTABLE_TYPE, PROP_NAME>): PrimitiveSwitch<MUTABLE_TYPE, PROP_NAME>
}
interface Hello {
foo: string;
bar: World;
}
interface World {
a: number;
b: symbol;
}
interface ImmutableHello extends Immutalizer<Hello> { };
interface ImmutableWorld extends Immutalizer<World> { };
let hello: ImmutableHello = Immutable.fromJS({});
let world: ImmutableWorld = hello.get("bar");
hello.get("bar").get("b"); // symbol
world.get("b"); // symbol
您丢失了接口的名称(hello.get("bar")
返回Immutalizer<World>
而不是ImmutableWorld
),但是类型仍然兼容。