TypeScript 中的函数重载与使用联合类型
Posted
技术标签:
【中文标题】TypeScript 中的函数重载与使用联合类型【英文标题】:Function Overloading in TypeScript vs Using a Union Type 【发布时间】:2021-02-20 18:11:41 【问题描述】:我正在阅读有关函数重载的信息。本质上它们是什么,假设您正在创建 3 个具有相同名称的函数,它们传递 3 个不同的参数和返回类型。我是 TS 的新手,我想知道以下问题:传递联合类型和返回联合类型不会相同吗?还是完全不同?
这就是我脑海中出现的例子。这行不行?
重载:
function f1(a: string)
function f1(a: number)
使用联合类型:
function f1(a: string | number):string | number
【问题讨论】:
函数重载是 TS 的纯编译时构造,因此您的双f1
函数声明将触发错误(JS 采用最后定义的函数,覆盖前一个)。
你是什么意思“这会工作”?第一个代码块是一个错误,因为您似乎两次实现了相同的功能。如果您想要重载,则需要多个调用签名和最多一个实现。重载通常表示输入和输出类型之间的关系。你的f1
似乎没有返回任何东西,所以我不知道该说什么,但大概一个返回string
,另一个返回number
。也许更多地充实这个想法,并有一些适合放入像The TypeScript Playground这样的IDE作为minimal reproducible example。
【参考方案1】:
函数重载将特定的输入类型映射到特定的返回类型。使用联合,您只知道返回是有效类型之一,但您失去了输入和输出之间的关联。这可能是也可能不是问题,具体取决于您使用该功能的方式和位置。但这就是区别。
这是重载的样子。重载签名中的最后一行不是重载之一,它描述了实现的参数。有时你会看到any
,但你也可以在这里使用联合。
function overloaded(a: string): string
function overloaded(a: number): number
function overloaded(a: any): any
return a;
不同的参数根据它们匹配的重载返回特定类型。
const oNum: number = overloaded(0);
const oStr: string = overloaded("");
const oBool = overloaded(true); //error
在我们的联合中,两种输入类型都只返回联合,所以我们失去了特异性。
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
示例现在可以使用。我们告诉 typescript “查看参数的类型 a
并称之为 T
”。然后我们得到这个类型变量T
,我们可以在返回类型中使用它。这里我们只是直接返回相同类型的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
【讨论】:
【参考方案2】:对于函数参数类型,函数重载可以比联合类型和泛型类型更具体。
现实世界的例子:
createActivity
函数用于创建具有不同输入参数的三种类型的活动。 CreateRenewalActivityParams
、CreateInsureActivityParams
和 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: [],
)
现在,我们希望函数的参数在同时包含 installmentCases
和 cases
字段时报告类型不兼容错误,因为它们属于不同的活动类型,这可以使用函数重载来完成。
我们可以用上述三种参数类型为createActivity
函数编写三个更具体的重载签名和一个CreateActivityParams
类型的通用实现签名。
TypeScript Playground
【讨论】:
【参考方案3】:只是扩展 @Linda Paiste 关于泛型类型的评论
const gNum: number = generic(0);
这基本上是在说
function generic<0>(a: 0): 0;
这有时可能不是您想要的。所以在使用泛型时,你要传入一个类型参数:
const gNum: number = generic<number>(0);
【讨论】:
以上是关于TypeScript 中的函数重载与使用联合类型的主要内容,如果未能解决你的问题,请参考以下文章