繁体   English   中英

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

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

我想知道是否有办法根据给它的输入键入 function 的返回值。 这是我在想什么的一个例子:

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

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

  return enumObject;
}

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

有没有办法输入这个 function 以便我们知道myEnum看起来像:

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

编辑:

正如@dariosicily 提到的,我们可以使用Record<string, string>或索引签名键入enumObject ,但我想知道是否有一种方法可以根据传入的参数知道返回 object 中存在的实际键。

一个内在的Uppercase<T>字符串操作实用程序类型,当给定一个string 文字类型作为输入时,它会生成一个大写版本 output。因此Uppercase<"abc">"ABC"是相同的类型。 使用这种类型,我们可以创建一个具有重新映射键的映射类型来表达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;
}

请注意, toEnum()K中是通用的,它是strings数组的元素类型的联合。 请注意, K限制string ,因此strings实际上是一个字符串数组,并且因为此限制给编译器一个提示,即我们要为其元素推断字符串文字类型,而不仅仅是string 你肯定需要在这里使用泛型,否则你只会从 function 中得到Record<string, string>

类型{[P in K as Uppercase<P>]: P}遍历原始K联合中的每个字符串P并将其重新映射为大写版本作为键,然后使用与值相同的类型P 那就是你想要的类型。

另请注意,我将enumObject any类型,以便在toEnum()的实现中选择退出严格类型检查; 编译器无法遵循enumObject[str.toUpperCase()]=str将是对{[P in K as Uppercase<P>]: P}类型值的适当操作的逻辑,因此我们甚至不会让它尝试。

无论如何你可以测试它是否做了你想要的:

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

在您提到的评论中,对于类似fortyFive的内容,您希望密钥为FORTY_FIVE而不是FORTYFIVE 也就是说,您不只是希望键是输入的大写版本。 您希望输入被解释为小驼峰大小写,而 output 被解释为全大蛇形大小写(也称为 SCREAMING_SNAKE_CASE)。

这在 TypeScript 中也是可能的,使用模板文字类型将字符串文字类型拆分为字符,并使用递归条件类型以编程方式对这些字符进行操作。

首先让我们在类型级别进行:

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

请注意,在值级别执行相同操作的 function 很有用:

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

类型和 function 的行为相似; 这个想法是迭代字符串的每个字符,当且仅当当前字符不是小写时插入下划线,然后插入当前字符的大写版本。 您可以验证这是否有效:

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

运行时的值和编译器计算的类型一致。

现在我们可以在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;
}

看看它的实际效果:

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

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

看起来挺好的!

游乐场代码链接

问题是由于在 javascript 语言中,行enumObject[str.toUpperCase()] = str; 分配是合法的 typescript 同一行会导致错误,因为您没有明确声明enumObject的索引签名或明确声明它为any

在这种情况下,解决问题的一种方法是使用内置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);

编辑:回答问题

有一种方法可以根据传入的参数知道返回 object 中存在的实际密钥

您可以使用Object.keys方法并将键作为Array<string>返回:

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

如果你想从键创建一个新类型,你可以使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM