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

TypeScript泛型拷贝函数类型报错:不用'as'如何解决?

问题描述

我找到了一些类似问题,但不知道怎么用更符合TypeScript风格的方式解决,不想用as类型断言。以下是我的问题示例:

错误一:Type '{}' is not assignable to type 'T'

function copy<T extends Record<string, any>>(target: T): T {
  const res: T = {} // Error 1: Type '{}' is not assignable to type 'T'.

  Object.keys(target).forEach(key => {
    if (typeof target[key] === 'object') res[key] = copy(target[key])
    else if (typeof target[key] === 'string') res[key] = target[key];
  });

  return res
}

错误二:Type 'Record<string, any>' is not assignable to type 'T'

function copy<T extends Record<string, any>>(target: T): T {
  const res: Record<string, any> = {};

  Object.keys(target).forEach(key => {
    if (typeof target[key] === 'object') res[key] = copy(target[key])
    else if (typeof target[key] === 'string') res[key] = target[key];
  });

  return res // Type 'Record<string, any>' is not assignable to type 'T'.
}

我目前用as断言解决了问题,代码如下:

function copy<T extends Record<string, any>>(target: T): T {
  const res: Record<string, any> = {};

  Object.keys(target).forEach(key => {
    if (typeof target[key] === 'object') res[key] = copy(target[key])
    else if (typeof target[key] === 'string') res[key] = target[key];
  });

  return res as T;
}

请问是否有其他解决方法?或者如何在T继承object或Record<string, any>时,实现传入T类型并返回T类型的函数?


解决方案

方法一:利用目标对象的构造函数创建实例

既然T是对象类型,通过target.constructor创建同类型的空实例,让TypeScript自动匹配类型:

function copy<T extends Record<string, any>>(target: T): T {
  // 创建与target同类型的空实例
  const res = new target.constructor() as T;

  Object.keys(target).forEach(key => {
    const value = target[key];
    // 排除null,因为typeof null会返回'object'
    if (typeof value === 'object' && value !== null) {
      res[key] = copy(value);
    } else if (typeof value === 'string') {
      res[key] = value;
    }
  });

  return res;
}

方法二:用Object.assign初始化结果

通过Object.assign将空对象合并为T类型变量,利用TypeScript的类型兼容性解决赋值问题:

function copy<T extends Record<string, any>>(target: T): T {
  // 先浅拷贝target的属性,初始化符合T类型的变量
  let res: T = Object.assign({}, target);

  Object.keys(res).forEach(key => {
    const value = res[key];
    if (typeof value === 'object' && value !== null) {
      res[key] = copy(value);
    }
  });

  return res;
}

方法三:定义递归深拷贝类型(更精确的类型约束)

通过自定义递归类型DeepCopy<T>,让类型推导更严谨,仅在必要场景使用类型断言:

// 定义递归的深拷贝类型
type DeepCopy<T> = T extends string 
  ? T 
  : T extends Record<string, any> 
    ? { [K in keyof T]: DeepCopy<T[K]> } 
    : T;

function copy<T extends string | Record<string, any>>(target: T): DeepCopy<T> {
  if (typeof target === 'string') {
    return target as DeepCopy<T>;
  }

  const res: Record<string, any> = {};
  Object.keys(target).forEach(key => {
    const value = target[key];
    if (typeof value === 'object' && value !== null) {
      res[key] = copy(value);
    } else {
      res[key] = value;
    }
  });

  return res as DeepCopy<T>;
}

内容的提问来源于stack exchange,提问作者JianCheng.Kang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:35:23