创建一个排除单个键的接口,其中所有其他动态键属于同一类型



我仍在学习TypeScript类型系统的所有复杂细节,想知道是否有人可以帮助我解决这个界面设计问题。

我希望在 TypeScript 中创建一个接口,其中一个或多个对象键属于通用类型,并且所有剩余键必须属于同一类型。我已经混合了条件类型和never类型联合,但没有提出可行的解决方案。

type Command = () => void;
type ViewState = {[key: string]: any};
export interface ViewModel<T extends ViewState> {
viewState: T;
// The following line is what does not work
[key: Omit<string, 'viewState']: Command;
}
// The result I am aiming for,
interface LabelState {
label: string;
}
interface LabelModel<LabelState> {
viewState: LabelState;
updateLabel: Command;
}
interface MenuState {
menuItems: string[];
}
interface MenuModel<MenuState> {
viewState: MenuState;
openMenu: Command;
closeMenu: Command;
};

我知道这不是必需的,但是,知道所有方法都是Command会很好.感觉这应该是可能的,有没有人确切地知道它是否可实现?如果是这样,我错过了什么?

这是解决此问题的一种可能方法:

type ViewModel<T> = {
[K in "viewState" | keyof T]: K extends "viewState" ? ViewState : Command
};

ViewModel类型不是在状态类型中是泛型的,而是在ViewModel本身的预期扩展中泛型的。 示例在这里可能更有意义:

interface LabelModel<S> extends ViewModel<LabelModel<S>> {
viewState: S;
updateLabel: Command;
}
interface MenuModel<S> extends ViewModel<MenuModel<S>> {
viewState: S;
openMenu: Command;
closeMenu: Command;
}

这些与LabelModelMenuModel类型相同,但extends ViewModel<...>强制执行您正在谈论的约束。 这种interface Foo extends ViewModel<Foo>构造称为 F 边界多态性,但基本上意味着您可以使接口引用其自己的类型。 TypeScript 实际上有一个名为"多态this"的功能,它可以让您在没有额外类型参数的情况下执行此操作,但它不能像我那样在映射类型中使用......哦,好吧。

无论如何,ViewModel<T>基本上采用T类型,并创建一个新类型,其中键不是"viewState"T的所有属性都具有Command值类型,并且还有一个ViewState类型的"viewState"属性。 因此,通过说interface X extends ViewModel<X>,我们说X必须可分配给唯一非Command属性viewState的东西。

让我们看看违反这一点以及会发生什么:

interface BadModel extends ViewModel<BadModel> { // error!
viewState: ViewState,
notACommand: string
} // Type 'string' is not assignable to type 'Command'.

因此,强制实施约束。 我希望这能满足您的需求,或者至少给您一个想法。 祝你好运!

链接到代码

相关内容

  • 没有找到相关文章

最新更新