简体   繁体   中英

How to conditionally set a function argument type in typescript?

I have a generic function I need to call in 2 places

const functionA = (arg1, deleteFunction) => {
 deleteFunction(arg1)
}

when I call it in the two different places I pass in a different deleteFunction each time. these deleteFunctions then update redux but they require different types so I'm getting errors

I was wondering if for arg1 I could specify what type it should be based on properties it contains. something like this

const functionA = (arg1: arg1.hasSomeProperty ? Arg1Types : Arg1OtherType, deleteFunction) => {
 deleteFunction(arg1)
}

obviously this doesn't work but the 2 deleteFunctions have different types (one has Arg1Types the other has Arg1OtherTypes

might be going about it in completely the wrong way. any ideas?

You can use a function overload, either using overload syntax with the function keyword, or using an interface with const an an arrow function as in your question.

Overload syntax:

function functionA(arg: Arg1Type, deleteFunction: (arg: Arg1Type) => void): void;
function functionA(arg: Arg1OtherType, deleteFunction: (arg: Arg1OtherType) => void): void;
function functionA(arg: any, deleteFunction: (arg: any) => void): void {
    deleteFunction(arg);
}

Playground Link

A function interface with const an an arrow function:

interface FunctionA {
    (arg: Arg1Type, deleteFunction: (arg: Arg1Type) => void): void;
    (arg: Arg1OtherType, deleteFunction: (arg: Arg1OtherType) => void): void;
}

const functionA: FunctionA = (arg: any, deleteFunction: (arg: any) => void): void => {
    deleteFunction(arg);
};

Playground link

In both cases, if Arg1Type is string and Arg1OtherType is number (for example), these calls work:

functionA("foo", (id) => {
    // ...do the deletion...
});

functionA(42, (id) => {
    // ...do the deletion...
});

...and these don't:

// Error: No overload matches this call.
// (because the types don't match)
functionA("foo", (id: number) => {
    // ...do the deletion...
    console.log(id);
});

// Error: No overload matches this call.
// (because no overload takes an object)
functionA({}, (id) => {
    // ...do the deletion...
    console.log(id);
});

And in both cases, only the overload signatures (the first two) will be shown by IDEs, etc.; the implementation signature isn't.

在此处输入图像描述

在此处输入图像描述

In a comment you've said:

...how the invoking of this functions knows which types to use? Arg1Type and Arg1OtherType are both objects but inside these objects, the types are differents for each property. ... I'd like to understand the conditional part a bit more

TypeScript will infer the correct overload to use based on the types of the arguments. In my examples, the types are string and number . When I started with functionA("foo", TypeScript could tell that I was using the string overload and will only allow a function that accepts a string. When I started with functionA(42, TypeScript could tell I was using the number overload and will only allow a function that accepts a number.

That's fine with objects with different shapes as well:

interface Arg1Type {
    prop: string;
}
interface Arg1OtherType {
    prop: number;
}

functionA({"prop": "foo"}, (obj) => {
    // ...do the deletion...
    console.log(obj);
});

functionA({"prop": 42}, (obj) => {
    // ...do the deletion...
    console.log(obj);
});

Playground Link

type A = string
type B = number
type Arg1 = A | B
const functionA = (arg1: Arg1, deleteFunc: (arg1: Arg1) => void): void {
 deleteFunc(arg1);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM