简体   繁体   中英

Typescript: Function return type that depends on value of string parameter

I'd like to write types for a JavaScript library that creates an object of functions depending on a string that is passed to it.

The function is supposed to be something like this:

function createResource(name: string): { /* How to write these types? */ } {
  return {
     [`create${name}`]: () => {},
     [`update${name}`]: () => {},
  }
}

The idea is that given a string to a function (say "todo"), the function would return an object

{
  createTodo: (todo: Todo) => void,
  updateTodo: (todo: Todo) => void
}

If/when microsoft/TypeScript#40336 is merged into master (hopefully for TS4.1) you will be able to use template string types to represent what happens when you concatenate "create" and "update" to the string passed in for name . It will look kind of like this:

function createResource<T extends string>(name: T) {
    return {
        [`create${name}`]: () => { },
        [`update${name}`]: () => { },
    } as { [K in `${"create" | "update"}${T}`]: () => void };
}

which produces an output like this:

const resource = createResource("Todo");
/* const resource: {
    createTodo: () => void;
    updateTodo: () => void;
} */

Note that `create${name}` does not produce "createTodo" unless name is "Todo" with a capital "T" . It is possible to use the capitalize modifier to turn "todo" into "Todo" in the type system, but your implementation would also need to change... something like:

const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
function createResource2<T extends string>(name: T) {
    return {
        [`create${cap(name)}`]: () => { },
        [`update${cap(name)}`]: () => { },
    } as { [K in `${"create" | "update"}${capitalize T}`]: () => void };
}

const resource2 = createResource2("todo");
/* const resource2: {
    createTodo: () => void;
    updateTodo: () => void;
} */

Playground link to code

Just define the object with implementations (I assume you would like to replace void with an actual implementation or a function call):

const functions = {
  createTodo: (todo: Todo) => void,
  updateTodo: (todo: Todo) => void
}

and then your createResource func will look like that:

function createResource(name: string) {
  const createFuncName = `create${name}`
  const updateFuncName = `update${name}`
  return {
     [createFuncName]: functions[createFuncName],
     [updateFuncName]: functions[updateFuncName]
  }
}

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