techguy2000
techguy2000

Reputation: 5171

How to specify typescript conditional type based on a function return type in the arguments

I have a function like this:

export const getValue = (
  options: Options = { someMapping, someFunction }
): string | MyType => {
  ...
  return someFunction({ ...someData });
};

Right now, the return type is string or MyType.

This is because someFunction can either return string or MyType.

Is there a way to use TypeScript conditional type to return the correct type based on what someFunction actually returns?

[Additional Info after first post]:

Here is the definition of Options type:

export interface Options {
  someMapping: MappingType;
  someFunc: (data: MyType) => MyType | string;
}

The someFunc param is a function that expects a MyType param.

Here are two actual functions I pass in depends on the situation:

const myFunc = (data: MyType): string => {
  return data.someProperty;
};

const myFunc = (data: MyType): MyType => {
  return data;
};

Upvotes: 0

Views: 1752

Answers (2)

DeeV
DeeV

Reputation: 36045

I think the closest you can do is type someFunction and use function overloading. The Options makes it a little more complicated, but something like this should work:

type MyFunc<T extends MyType | string> = () => T;

interface Options<T extends MyType | string> {
  someMapping: WhateverTypeThisIs;
  someFunc: MyFunc<T>;
}

function getValue(options: Options<MyType>): MyType;
function getValue(options: Options<string>): string;
function getValue<T extends MyType | string>(options: Options<T>): T {
   return options.someFunc();
}

So now you should get typings for something like this:

const myTypeOptions: Options<MyType> = {
   someMapping: {},
   someFunc: () => ({ myParam: "MyValue" })
};
const myStringOptions: Options<string> = {
   someMapping: {},
   someFunc: () => "Hello"
};
const myTypeValue = getValue(myOptions); // myNumberValue is of type { myParam: string };
const myStringValue = getValue(myStringOptions); // myStringValue is a string;

This is of course dependent on the fact that the function in question always returns a MyType or always returns a string. If it changes depending on the parameters of the function, then it will always be a MyType | string because Typescript would not be able to determine that. It would be up the caller to figure out what it is.

EDIT:

Based on the scenario you presented, something like this might help. You can create to types of MyFunc.

type MyTypeFunc = (myType: MyType) => MyType;
type MyStringFunc = (myType: MyType) => string;

interface MyOptions {
  someMapping: WatheverTypeThisIs;
}

interface MyTypeOptions extends MyOptions {
  someFunc: MyTypeFunc;
}

interface MyStringOptions extends MyOptions {
  someFunc: MyStringFunc;
}

function getValue(options: MyTypeOptions): MyType;
function getValue(options: MyStringOptions): string;
function getValue(options: MyTypeOptions | MyStringOptions): T {
   return options.someFunc();
}

Now, whenever you call it you can create or options like this:

const myTypeOptions: MyTypeOptions = {
  someMapping: {},
  someFunc: (myType) => myType
}

const myStringOptions: MyStringOptions = {
  someMapping: {},
  someFunc: (myType) => myType.someProperty
}

const myTypeValue = getValue(myTypeOptions); // myTypeValue will be of type MyType
const myStringValue = getValue(myStringOptions); // myStringValue will be of type string

Upvotes: 1

Pavel Kratochvil
Pavel Kratochvil

Reputation: 481

playground you can let typescript infer return type. Function overloading provided by @DeeV works as well.

Upvotes: 0

Related Questions