我希望打字稿接受具有相似结构但值互不相同的参数列表,并正确推断出生成的联合类型。
示例代码
interface User<
N extends string = string,
S extends unknown = unknown,
A extends (a: S) => unknown = (a: S) => unknown
> {
name: N;
state: S;
action: A;
}
function createUser<
N extends string = string,
S extends unknown = unknown,
A extends (a: S) => unknown = (a: S) => unknown
>(name: N, state: S, action: A): User<N, S, A> {
return { action, name, state };
}
function combineUsers<
V extends User<string, unknown, (a: unknown) => unknown>[]
>(...users: V) {
type Names = V[number]["name"];
return {
actions: users.reduce(
(a, u) => ({ ...a, [u.name]: u.action }),
{} as { [K in Names]: Extract<V[number], { name: K }>["action"] },
),
names: new Set<Names>(users.map((u) => u.name)),
};
}
const userA = createUser("user1", 1, (_a: number) => undefined);
const user2 = createUser("userB", "two", (_s: string) => true);
const users = combineUsers(userA, user2);
// Argument of type 'User<"user1", number, (_a: number) => undefined>' is not assignable to parameter of type 'User<string, unknown, (a: unknown) => unknown>'.
// Type '(_a: number) => undefined' is not assignable to type '(a: unknown) => unknown'.
// Types of parameters '_a' and 'a' are incompatible.
// Type 'unknown' is not assignable to type 'number'.ts(2345)
那是由于功能不兼容而导致的编译错误。
我希望看到函数返回的类型是一些推断的联合,例如;
type Return = {
names: Set<"user1" | "userB">,
actions: {
user1: (_a: number) => undefined;
userB: (_s: string) => boolean;
}
}
any
,但我不愿意编辑:
@jcalz的解决方案对于上一个版本中所述的问题非常有效,但是我的示例缺少使它适用于我的实际代码的内容,即函数的参数必须与在其他地方定义的类型匹配时。
答案 0 :(得分:1)
我要为combinedUsers
输入的内容如下:
function combineUsers<V extends User<string, never[], unknown>[]>(...users: V) {
type Names = V[number]['name'];
return {
actions: users.reduce(
(a, u) => ({ ...a, [u.name]: u.action }),
{} as { [K in Names]: Extract<V[number], { name: K }>['action'] }
),
names: new Set<Names>(users.map((u) => u.name)),
};
}
请注意,只有一个通用类型参数V
对应于传入的User<N, A, R>
对象的数组。类型参数string
(属性类型的协变上限约束), never[]
(参数类型的协变下界约束)和unknown
(返回值类型的协变上界约束)比any
,any
,{ {1}},但仍然可以允许一些奇怪的输入。希望这实际上并不重要。
在实现中,我们定义any
类型,以获取Names
每个元素的N
参数的并集(V
是您在{ {1}},索引为V[number]['name']
,然后索引为结果的users
,索引为number
。
User
属性的类型为映射类型,其中对于"name"
中的每个名称,我们找到actions
的元素,并将其作为Names
,得到其V
类型。
让我们看看它是否有效。假设您有以下类型的name
和action
:
userA
然后您user2
导致以下结果:
//const userA: User<"user1", [x: number, y: string], undefined>
//const user2: User<"userB", [x: string], boolean>
看起来不错。