简体   繁体   中英

How can I narrow down the type of a function argument without a variable

I have a function that accepts any as parameter:

declare function postMessage(message: any): void;

I'm trying to call it with an object of certain type as follows:

interface NumMsg {type: 'num'; num: number}
interface StrMsg {type: 'str'; str: string}

type Msg = NumMsg | StrMsg;

postMessage({type: 'num', num: 42} as Msg);
postMessage({type: 'str', str: 'Hello World!'} as Msg);

It kind of works but it makes some of the required properties optional - it builds successfully without any errors:

// Works fine / fails to build:
postMessage({type: 'num', 'str': 'Hello world!'} as Msg);
postMessage({type: 'str', 'num': 42} as Msg);

// Doesn't work fine / build without errors:
postMessage({});
postMessage({type: 'str'} as Msg);
postMessage({type: 'num'} as Msg);

I can solve that issue by defining it as a variable first:

let msg1: Msg = {};
let msg2: Msg = {type: 'str'};
let msg3: Msg = {type: 'num'};

Can I make this work without defining any variables?

I'm assuming you can't change the type of postMessage (for instance, that it's the postMessage defined in the web platform or similar).

I'd solve this by having a wrapper function, not a variable, and not using postMessage directly at all:

function postMsg(message: NumMsg | StrMsg) {
    postMessage(message);
}

(But with a better function name. :-) )

Then these work:

postMsg({type: "num", num: 42});
postMsg({type: "str", str: "Hellow world!"});

...and these cause errors as desired:

postMsg({});
postMsg({type: "str"});
postMsg({type: "num"});
postMsg({type: "num", "str": "Hello world!"});
postMsg({type: "str", "num": 42});

Playground link


If you can change the types of postMessage , just do it directly:

declare function postMessage(message: NumMsg | StrMsg): void;

In the end, I decided to add an overload to the said function: ()

declare function postMessage<T>(message: T): void;

Credits to @jonrsharpe .

Now I have the following:

declare function postMessage(message: any): void;
declare function postMessage<T>(message: T): void;

It works even though the former signature is/seems more general.

Now I can do the following without any issues:

postMessage<Msg>({type: 'num', 'str': 'Hello world!'});
postMessage<Msg>({type: 'str', 'num': 42});

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