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

如何让TypeScript的Partial类型参数可不含任何可选字段?

问题:允许函数参数包含/不包含指定接口字段,同时支持任意额外字段

简化场景复现

以下代码中,我们希望函数baz接收的参数可以包含Foo的字段,也可以完全不包含,但当前TypeScript会在参数无Foo字段时报错:

interface Foo {
  foo: string;
}

function baz(x: Partial<Foo>) {
  return x;
}

// 合法:包含Foo字段+额外字段
const args1 = { foo: '1', bar: 1 };
baz(args1);

// 报错:Type '{ bar: number; }' has no properties in common with type 'Partial<Foo>'
const args2 = { bar: 1 };
baz(args2);

实际业务场景

在React组件的props合并逻辑中,需要接收任意组件props(可包含DraggableChildProps的className、style等字段,也可完全不包含),合并后返回最终props:

function mergeDraggableProps(
  draggableProps: DraggableChildProps,
  childProps: Partial<DraggableChildProps>
) {
  return {
    ...childProps,
    className: cn(draggableProps.className, childProps.className),
    style: {
      ...draggableProps.style,
      ...childProps.style,
    },
    transform: draggableProps.transform,
    onMouseDown: draggableProps.onMouseDown,
    onMouseUp: draggableProps.onMouseUp,
    onTouchEnd: draggableProps.onTouchEnd,
  };
}

问题:当childProps完全不包含DraggableChildProps的字段时,TypeScript会抛出相同的「无共同属性」错误。


原始泛型版本(供参考)

此前尝试用泛型实现,同样存在该问题:

interface Foo {
  foo: string;
}

function baz<T extends Partial<Foo>>(x: T) {
  return x;
}

// 合法
const args1 = { foo: '1', bar: 1 };
baz(args1);

// 报错
const args2 = { bar: 1 };
baz(args2);

对应的业务代码泛型版本:

function mergeDraggableProps<T extends Partial<DraggableChildProps>>(
  draggableProps: DraggableChildProps,
  childProps: T
): T & DraggableChildProps {
  return {
    ...childProps,
    className: cn(draggableProps.className, childProps.className),
    style: {
      ...draggableProps.style,
      ...childProps.style,
    },
    transform: draggableProps.transform,
    onMouseDown: draggableProps.onMouseDown,
    onMouseUp: draggableProps.onMouseUp,
    onTouchEnd: draggableProps.onTouchEnd,
  };
}

解决方案

通过交叉类型结合索引签名,让参数类型同时支持「可选的目标接口字段」和「任意额外字段」,完全规避无共同属性的报错:

简化场景修复

interface Foo {
  foo: string;
}

// 定义类型:可包含Foo的部分字段,也可以是任意对象(无Foo字段也合法)
type FooAcceptable = Partial<Foo> & Record<string, unknown>;

function baz(x: FooAcceptable) {
  return x;
}

// 全部合法
const args1 = { foo: '1', bar: 1 };
baz(args1);
const args2 = { bar: 1 };
baz(args2);
baz({});

业务场景修复

调整childProps的类型定义即可:

// 支持任意字段 + 可选的DraggableChildProps字段
type AcceptableChildProps = Partial<DraggableChildProps> & Record<string, unknown>;

function mergeDraggableProps(
  draggableProps: DraggableChildProps,
  childProps: AcceptableChildProps
) {
  return {
    ...childProps,
    className: cn(draggableProps.className, childProps.className),
    style: {
      ...draggableProps.style,
      ...childProps.style,
    },
    transform: draggableProps.transform,
    onMouseDown: draggableProps.onMouseDown,
    onMouseUp: draggableProps.onMouseUp,
    onTouchEnd: draggableProps.onTouchEnd,
  };
}

如果需要保留泛型(比如推导返回类型),可以修改泛型约束:

function mergeDraggableProps<T extends Record<string, unknown>>(
  draggableProps: DraggableChildProps,
  childProps: T & Partial<DraggableChildProps>
): T & DraggableChildProps {
  return {
    ...childProps,
    className: cn(draggableProps.className, childProps.className),
    style: {
      ...draggableProps.style,
      ...childProps.style,
    },
    transform: draggableProps.transform,
    onMouseDown: draggableProps.onMouseDown,
    onMouseUp: draggableProps.onMouseUp,
    onTouchEnd: draggableProps.onTouchEnd,
  };
}

原理说明

TypeScript的Partial<T>在严格模式下要求参数至少与T有一个共同属性,而Record<string, unknown>允许任意键值对的对象。两者交叉后,既保留了Partial<T>的字段可选性,又允许完全不包含T字段的任意对象传入,彻底解决报错问题。

内容的提问来源于stack exchange,提问作者Andrew Schwartz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:03:19