简体   繁体   English

Typescript:键入 Function 基于参数的返回类型

[英]Typescript: Typing Function Return Type based on Parameters

I was wondering if there is way to type the return of a function based on the input given to it.我想知道是否有办法根据给它的输入键入 function 的返回值。 Here is an example of what I am thinking:这是我在想什么的一个例子:

function toEnum(...strings: string[]) {
  const enumObject = {};

  strings.forEach((str) => {
    enumObject[str.toUpperCase()] = str;
  });

  return enumObject;
}

const myEnum = toEnum('one', 'two', 'three')

Is there a way to type this function so that we know that myEnum looks like:有没有办法输入这个 function 以便我们知道myEnum看起来像:

{
  ONE: 'one',
  TWO: 'two',
  THREE: 'three'
}

edit:编辑:

as @dariosicily mentioned, we could type enumObject using a Record<string, string> or index signatures, but I am wondering if there is a way to know the actual keys present in the return object based on the params passed in.正如@dariosicily 提到的,我们可以使用Record<string, string>或索引签名键入enumObject ,但我想知道是否有一种方法可以根据传入的参数知道返回 object 中存在的实际键。

There is an intrinsic Uppercase<T> string manipulation utility type which, when given a string literal type as input, produces an uppercase version as output. So Uppercase<"abc"> and "ABC" are the same type.一个内在的Uppercase<T>字符串操作实用程序类型,当给定一个string 文字类型作为输入时,它会生成一个大写版本 output。因此Uppercase<"abc">"ABC"是相同的类型。 Using this type we can create a mapped type with remapped keys to express the output type of toEnum() , given the union of the string literal types of its arguments:使用这种类型,我们可以创建一个具有重新映射键的映射类型来表达toEnum()的 output 类型,给定其arguments的字符串文字类型的并集:

function toEnum<K extends string>(...strings: K[]): { [P in K as Uppercase<P>]: P } {
    const enumObject: any = {};

    strings.forEach((str) => {
        enumObject[str.toUpperCase()] = str;
    });

    return enumObject;
}

Note that toEnum() is generic in K , the union of the element types of the strings array.请注意, toEnum()K中是通用的,它是strings数组的元素类型的联合。 Note that K is constrained to string so that strings is indeed an array of strings, and because this constraint gives the compiler a hint that we want to infer string literal types for its elements instead of just string .请注意, K限制string ,因此strings实际上是一个字符串数组,并且因为此限制给编译器一个提示,即我们要为其元素推断字符串文字类型,而不仅仅是string You definitely need to use generic here, otherwise you'd just get Record<string, string> out of the function.你肯定需要在这里使用泛型,否则你只会从 function 中得到Record<string, string>

The type {[P in K as Uppercase<P>]: P} iterates over every string P in the original K union and remaps it to an uppercase version as the key, and then uses just the same type P as the value.类型{[P in K as Uppercase<P>]: P}遍历原始K联合中的每个字符串P并将其重新映射为大写版本作为键,然后使用与值相同的类型P That's the type you wanted.那就是你想要的类型。

Also note that I gave enumObject the any type so as to opt out of strict type checking inside the implementation of toEnum() ;另请注意,我将enumObject any类型,以便在toEnum()的实现中选择退出严格类型检查; the compiler is unable to follow the logic that enumObject[str.toUpperCase()]=str will be an appropriate operation on a value of type {[P in K as Uppercase<P>]: P} , so we won't even make it try.编译器无法遵循enumObject[str.toUpperCase()]=str将是对{[P in K as Uppercase<P>]: P}类型值的适当操作的逻辑,因此我们甚至不会让它尝试。

Anyway you can test that it does what you want:无论如何你可以测试它是否做了你想要的:

const myEnum = toEnum('one', 'two', 'three', "fortyFive");
/* const myEnum: {
    ONE: "one";
    TWO: "two";
    THREE: "three";
    FORTYFIVE: "fortyFive";
} */

console.log(myEnum.THREE) // "three" both at compile and runtime

In the comments you mentioned that for something like fortyFive , you'd like the key to be FORTY_FIVE instead of FORTYFIVE .在您提到的评论中,对于类似fortyFive的内容,您希望密钥为FORTY_FIVE而不是FORTYFIVE That is, you don't just want the key to be an uppercase version of the input.也就是说,您不只是希望键是输入的大写版本。 You want the input to be interpreted as lower camel case and the output to be all-upper snake case (also known as SCREAMING_SNAKE_CASE).您希望输入被解释为小驼峰大小写,而 output 被解释为全大蛇形大小写(也称为 SCREAMING_SNAKE_CASE)。

This is also possible in TypeScript, using template literal types to split a string literal type into characters, and recursive conditional types to operate on these characters programmatically.这在 TypeScript 中也是可能的,使用模板文字类型将字符串文字类型拆分为字符,并使用递归条件类型以编程方式对这些字符进行操作。

First let's do it at the type level:首先让我们在类型级别进行:

type LowerPascalToUpperSnake<T extends string, A extends string = ""> =
    T extends `${infer F}${infer R}` ? LowerPascalToUpperSnake<R,
        `${A}${F extends Lowercase<F> ? "" : "_"}${Uppercase<F>}`
    > : A;

Note that it is useful to have a function that does the same thing at the value level:请注意,在值级别执行相同操作的 function 很有用:

function lowerPascalToUpperSnake<T extends string>(str: T) {
    return str.split("").map(
        c => (c === c.toLowerCase() ? "" : "_") + c.toUpperCase()
    ).join("") as LowerPascalToUpperSnake<T>
}

Both the type and the function behave similarly;类型和 function 的行为相似; the idea is to iterate over each character of the string, insert an underscore if and only if the current character is not lowercase, and then insert an uppercase version of the current character.这个想法是迭代字符串的每个字符,当且仅当当前字符不是小写时插入下划线,然后插入当前字符的大写版本。 You can verify that this works:您可以验证这是否有效:

const test = lowerPascalToUpperSnake("abcDefGhiJklmNop");
// const test: "ABC_DEF_GHI_JKLM_NOP"
console.log(test); // "ABC_DEF_GHI_JKLM_NOP" 

The value at runtime and the type computed by the compiler agree.运行时的值和编译器计算的类型一致。

And now we can use the "lower-Pascal-to-upper-snake" operation in toEnum() instead of the original uppercase operation:现在我们可以在toEnum()中使用“lower-Pascal-to-upper-snake”操作来代替原来的大写操作:

function toEnum<K extends string>(...strings: K[]): 
  { [P in K as LowerPascalToUpperSnake<P>]: P } {
    const enumObject: any = {};

    strings.forEach((str) => {
        enumObject[lowerPascalToUpperSnake(str)] = str;
    });

    return enumObject;
}

And see it in action:看看它的实际效果:

const myEnum = toEnum('one', 'two', 'three', "fortyFive");
/* const myEnum: {
    ONE: "one";
    TWO: "two";
    THREE: "three";
    FORTY_FIVE: "fortyFive";
} */

console.log(myEnum.FORTY_FIVE) // "fortyFive"

Looks good!看起来挺好的!

Playground link to code 游乐场代码链接

The problem is due to the fact that while in the javascript language the line enumObject[str.toUpperCase()] = str;问题是由于在 javascript 语言中,行enumObject[str.toUpperCase()] = str; assignment is legitimate with typescript the same line causes an error because you have not explicitely declared the index signature of the enumObject or explicitely declared it as any .分配是合法的 typescript 同一行会导致错误,因为您没有明确声明enumObject的索引签名或明确声明它为any

In this cases one way to solve the issue is use the builtinRecord utility type applying it to your enumObject like below:在这种情况下,解决问题的一种方法是使用内置Record实用程序类型将其应用于您的enumObject ,如下所示:

function toEnum(...strings: string[]) {
    const enumObject: Record<string, string> = {};
    strings.forEach((str) => {
        enumObject[str.toUpperCase()] = str;
    });
    return enumObject;
}

const myEnum = toEnum('one', 'two', 'three')
//it will print { ONE: 'one', TWO: 'two', THREE: 'three' }
console.log(myEnum);

Edit : answering to the question编辑:回答问题

there is a way to know the actual keys present in the return object based on the params passed in有一种方法可以根据传入的参数知道返回 object 中存在的实际密钥

You can use the Object.keys method and return the keys as an Array<string> :您可以使用Object.keys方法并将键作为Array<string>返回:

const myEnum = toEnum('one', 'two', 'three')
//it will print [ONE, TWO, THREE]
const keys = (Object.keys(myEnum) as Array<string>);

If you want to create a new type from keys you can use typeof :如果你想从键创建一个新类型,你可以使用typeof

const keys = (Object.keys(myEnum) as Array<string>);
//type KeysType = 'ONE' | 'TWO' | 'THREE'
type KeysType = typeof keys[number];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 TypeScript - 基于多个参数的函数返回类型 - TypeScript - Function return type based on multiple parameters 在 Typescript 中,有没有办法使 function 参数强制输入 - In Typescript, is there a way to make function parameters typing mandatory 如何让 typescript 根据参数返回正确的类型 - How to let typescript return correct type based on parameters Typescript:键入带有函数数组的 function,该函数返回每个 function 返回类型的数组 - Typescript: typing a function with an array of functions that returns an array of each function return type typescript:根据回调返回类型验证函数参数 - typescript: validate function argument based on callback return type TypeScript:你能根据函数的参数定义一个返回类型结构吗? - TypeScript: Can you define a return Type structure based on an argument of the function? 键入获取的返回类型时出现问题 function - Having issue with typing a return type of fetching function 如何在打字稿打字时支持从lodash扩展函数中的无限参数? - How to support unlimited parameters in extend function from lodash in typing of typescript? 使用 typescript 中的通用类型从 object 返回具有正确类型的属性 - Return property from object with correct type using generic typing in typescript 打字稿中的覆盖函数返回类型 - Override function return type in typescript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM