有没有更好的方法来构建这个打字稿类型定义?

Is there a better way of structuring this typescript type definition?

我刚开始在我的 Node 项目中使用 TypeScript,我想知道是否有更简洁、更简洁的实现方式:

import { XOR } from "ts-xor";

type _RemoveNull<T> = {
    [P in keyof T] : string;
}

type UserIdParam = {
    a: string;
}

type BudgetIdParam = UserIdParam & {
    b: string | null;
}

type AccountIdParam = _RemoveNull<BudgetIdParam> & {
    c: string | null;
}

type TransIdParam = _RemoveNull<AccountIdParam> & {
    d: string | null;
}

type IdsParam = XOR<XOR<XOR<UserIdParam, BudgetIdParam>, AccountIdParam>, TransIdParam>;

我想要一个可以接受任何这些示例对象的类型:

const a = {a: "1"};
const b = {a: "1", b: "2"};
const c = {a: "1", b: "2", c: "3"};
const d = {a: "1", b: "2", c: "3", d: "4"};

此外,只有对象的最后一个可用属性可以为null,这就是为什么我必须与之前的类型相交并从联合中删除null。 我尝试对 UserIdParamBudgetIdParamAccountIdParamTransIdParam 这四种类型进行联合,但在阅读了 等其他问题后,我决定使用异或 (ts-xor) 来完成我需要的。

请告诉我您的想法。谢谢!

--

编辑:正如@Thomas 在评论中提到的,对象属性没有顺序的概念,因此没有“最后”一个。

我能够使用泛型、映射类型和条件类型重新实现它(感谢这个 article 为我指明了正确的方向):

type UserIdKeys = "userId";
type BudgetIdKeys = UserIdKeys | "budgetId" ;
type AccountIdKeys = BudgetIdKeys | "accountId";
type TransactIdKeys = AccountIdKeys | "transactionId";

// added for readability
type AllIdKeys = TransactIdKeys;

type IdParamGroup<T extends AllIdKeys, N extends AllIdKeys> = {
    [P in T]: P extends N ? (string | null) : string;
};

这是一个用法示例:

// valid
const trans1: IdParamGroup<TransactIdKeys, "transactionId"> = {
    userId: "1",
    budgetId: "3",
    accountId: "56",
    transactionId: null,
}
// invalid
const budget3: IdParamGroup<BudgetIdKeys, "budgetId"> = {
    userId: "1",
    budgetId: null,
    accountId: "23"
}

您可以在这里尝试:TS Playground