TypeScript 中的函数重载与使用联合类型

Function Overloading in TypeScript vs Using a Union Type

我正在阅读有关函数重载的内容。本质上它们是,你正在创建比方说 3 个具有相同名称的函数,它们传递 3 个不同的参数和 return 类型。我是 TS 的新手,我想知道以下问题:传递联合类型和 returning 联合类型不会相同吗?还是完全不同的东西?

这就是我脑海中出现的例子。这行不行?

过载:

function f1(a: string) {
}
function f1(a: number) {
}

使用联合类型:

function f1(a: string | number):string | number  {

}

函数重载将特定输入类型映射到特定 return 类型。使用联合,您只知道 return 是有效类型之一,但您失去了输入和输出之间的关联。这可能是问题,也可能不是问题,具体取决于您使用该功能的方式和位置。但这就是区别。

这是重载的样子。重载签名中的最后一行不是重载之一,它描述了实现的参数。有时你会看到 any,但你也可以在这里使用并集。

function overloaded(a: string): string
function overloaded(a: number): number
function overloaded(a: any): any {
    return a;
}

不同的参数 return 基于它们匹配的重载的特定类型。

const oNum: number = overloaded(0);
const oStr: string = overloaded("");
const oBool = overloaded(true); //error

在我们的联合中,两种输入类型都只是 return 联合,所以我们失去了特异性。

function union(a: string | number): string | number {
    return a;
}
const uNum: string | number = union(0);
const uStr: string | number = union("");
const uBool = union(true); //error

还有第三个选项typescript generics。这使我们能够在接受无限多种类型的同时保持特异性。 boolean 示例现在可以使用了。我们告诉打字稿“查看参数的类型 a 并调用它 T”。然后我们得到这个类型变量 T ,我们可以在 return 类型中使用它。在这里我们只是直接 returning 相同的类型 T,但是你可以用它做很多事情。

function generic<T>(a: T): T {
    return a;
}
const gNum: number = generic(0);
const gStr: string = generic("");
const gBool: boolean = generic(true);

Typescript Playground Link

只是扩展@Linda Paiste 关于泛型类型的评论

const gNum: number = generic(0);

这基本上就是在说

function generic<0>(a: 0): 0;

有时这可能不是您想要的。所以在使用泛型的时候,要传入一个类型参数:

const gNum: number = generic<number>(0);

对于函数参数类型,函数重载可以比联合类型和泛型类型更具体。

现实世界的例子:

createActivity函数用于创建三种输入参数不同的活动。 CreateRenewalActivityParamsCreateInsureActivityParams CreateTrafficGenerationActivityParams

interface CreateActivityParams {
  activityCode: string;
  activityName: string;
  activityDesc: string;
  activityType: number;
  activityTypeName: string;
  startTime: string;
  endTime: string;
  paymentType: string | null;
}
interface CreateRenewalActivityParams extends CreateActivityParams {
  installmentCases: any[];
}

interface CreateInsureActivityParams extends CreateActivityParams {
  cases: any[];
}

interface CreateTrafficGenerationActivityParams extends CreateActivityParams {
  cases: any[];
}
export async function createActivity(data: CreateTrafficGenerationActivityParams): Promise<boolean>;
export async function createActivity(data: CreateInsureActivityParams): Promise<boolean>;
export async function createActivity(data: CreateRenewalActivityParams): Promise<boolean>;
export async function createActivity(data: CreateActivityParams): Promise<boolean> {
  return true;
}

// TSC throw error
createActivity({
  activityCode: 'test code',
  activityName: 'test name',
  activityDesc: 'test desc',
  activityType: 8,
  activityTypeName: 'test type name',
  startTime: '2021-07-20 00:00:00',
  endTime: '2025-07-20 23:59:59',
  paymentType: null,
  cases: [
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
  ],
  installmentCases: [],
});


export async function createActivityUnion(data: CreateTrafficGenerationActivityParams | CreateInsureActivityParams | CreateRenewalActivityParams) {
    return true;
}

// TSC pass
createActivityUnion({
  activityCode: 'test code',
  activityName: 'test name',
  activityDesc: 'test desc',
  activityType: 8,
  activityTypeName: 'test type name',
  startTime: '2021-07-20 00:00:00',
  endTime: '2025-07-20 23:59:59',
  paymentType: null,
  cases: [
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
  ],
  installmentCases: [],
});

export async function createActivityGeneric<Data extends CreateActivityParams>(data: Data) {
    return true;
}

// TSC pass
createActivityGeneric({
  activityCode: 'test code',
  activityName: 'test name',
  activityDesc: 'test desc',
  activityType: 8,
  activityTypeName: 'test type name',
  startTime: '2021-07-20 00:00:00',
  endTime: '2025-07-20 23:59:59',
  paymentType: null,
  cases: [
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
    {
      pictureCode: 'test pic code',
      authDesc: null,
    },
  ],
  installmentCases: [],
})

现在,我们希望函数的参数在同时包含installmentCasescases字段时报类型不兼容错误,因为它们属于不同的activity类型,可以是使用函数重载完成。

我们可以为 createActivity 函数编写三个更具体的重载签名,其中包含上述三种参数类型和 CreateActivityParams 类型的通用实现签名。

TypeScript Playground