简体   繁体   中英

How to return type based on the object properties parameter with generic type

I want to return a promise if the user set true for async.

is seems typescript will always take the type from Create interface. how will I create a type that will be used to define the return type?

interface Create<T> {
  options: {
    async: boolean;
  };
  properties: T;
}

type Return<T extends Create<any>> = T["options"]["async"] extends true ? Promise<boolean> : boolean;

const create = <T>(value: Create<T>): Return<Create<T>> => {
  return true;
};

interface User {
  username: string;
  password: string;
}

// is not promise
create<User>({
  options: {
    async: true,
  },
  properties: {
    username: "",
    password: "",
  },
});

You just need one additional generic type parameter for the async option so that you can use it to infer the correct return type:

TS Playground

type Create<Properties, IsAsync extends boolean> = {
  options: { async: IsAsync };
  properties: Properties;
};

type CreateResult<IsAsync extends boolean> = IsAsync extends true ? Promise<boolean> : boolean;

declare function create <Properties, IsAsync extends boolean>(
  input: Create<Properties, IsAsync>,
): CreateResult<IsAsync>;

type User = {
  username: string;
  password: string;
};

declare const userProps: User;

// Using the literal `true` results in a Promise:
const r1 = create({ options: {async: true}, properties: userProps });
    //^? const r1: Promise<boolean>

// Using the literal `false` results in a boolean:
const r2 = create({ options: {async: false}, properties: userProps });
    //^? const r2: boolean

// Using an indeterminate boolean variable results in a union of either possibility:
declare let isAsync: boolean;
const r3 = create({ options: {async: isAsync}, properties: userProps });
    //^? const r3: boolean | Promise<boolean>


Update in response to your comment :

TS Playground

declare function create <IsAsync extends boolean>(
  input: {
    options: { async: IsAsync };
    properties: unknown;
  },
): IsAsync extends true ? Promise<boolean> : boolean;

This is the best I can do. I don't like having to use a type assertion on the return, but I couldn't find a way around it.

interface Create<T> {
  options: {
    async: boolean;
  };
  properties: T;
}

type Return<T> = T extends { options: { async: true } } ? Promise<boolean> : boolean;

const create = <T extends Create<any>>(value: T): Return<T> => {
  if (value.options.async === true) {
    return new Promise<boolean>((resolve) => resolve(true)) as Return<T>;
  }
  return true as Return<T>;
};

interface User {
  username: string;
  password: string;
}

create<Create<User>>({
  options: {
    async: true,
  },
  properties: {
    username: '',
    password: '',
  },
});

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