You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何将类型参数约束为另一类型的子集?

问题描述

我定义了表示应用所有服务的类型:

type Services = {
    service0: () => string;
    service1: () => string;
}

想要编写一个函数,仅接受Services的子集类型——也就是只能包含Services里的0个或多个字段,不能有额外字段。比如以下类型是符合要求的:

type Subset = {
    service0: () => string;
}

我尝试用Partial<Services>实现:

const someFn = <S extends Partial<Services>>(subset: S) => {
    console.log("some function");
}

这种写法在合法场景下能正常工作,但问题是它会接受带额外字段的类型,比如:

type Subset = {
    service0: () => string;
    lofasz: number;
}

someFn<Subset>({
    service0: () => "foo",
    lofasz: 1
})

这种不符合要求的调用也能通过类型检查,请问怎么约束函数参数,让它只接受和Services有共同字段的类型?

解决方案

可以通过以下几种方式实现严格约束:

方法1:用Pick限定字段范围

让泛型参数限定为Services的键名集合,再用Pick生成合法的子集类型,这是最直观的方案:

type Services = {
    service0: () => string;
    service1: () => string;
}

const someFn = <K extends keyof Services>(subset: Pick<Services, K>) => {
    console.log("some function");
}

调用示例

  • 合法调用:
    someFn({ service0: () => "foo" });
    someFn({ service1: () => "bar" });
    someFn({}); // 空对象也允许
    
  • 非法调用(会触发类型报错):
    someFn({ service0: () => "foo", lofasz: 1 });
    

如果要显式指定泛型参数,也只能传入Services的键名:

someFn<'service0'>({ service0: () => "foo" }); // 合法
// someFn<'service0' | 'lofasz'>({ ... }); // 报错,'lofasz'不是Services的键

方法2:通过类型约束排除额外属性

如果希望保留泛型参数为子集类型本身,可以约束子集的所有键都必须是Services的键:

const someFn = <S extends { [K in keyof S]: K extends keyof Services ? Services[K] : never }>(subset: S) => {
    console.log("some function");
}

这种写法会对额外字段的类型标记为never,从而触发类型错误。

方法3:定义严格子集类型

先定义一个严格的子集类型,直接排除所有不属于Services的字段,再用这个类型约束函数参数:

type StrictSubset<T> = Partial<T> & { [K: string]: never };

const someFn = (subset: StrictSubset<Services>) => {
    console.log("some function");
}

这种方式不需要泛型,直接限制参数只能包含Services的字段,不能有额外属性。

内容的提问来源于stack exchange,提问作者Adam A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:45:13