【问题标题】:Convert a type name to the actual type将类型名称转换为实际类型
【发布时间】:2020-04-06 19:56:57
【问题描述】:

假设我们有以下枚举

enum PrimayValType {
  "string",
  "boolean",
  "integer",
  "float"
}

现在我想编写一个函数,输入一个Buffer 和一个PrimaryValType 类型的参数,并根据PrimaryValType 转换缓冲区。如何在 Typescript 中编写这样的函数?

function f(b: Buffer, t: PrimayValType): ??? {
    // converts b to a literal based on t
}

const b = Buffer.from('test', 'utf-8');
f(b, PrimayValType.string) // should be of type string

【问题讨论】:

  • 你可以定义像function f(b: Buffer, t: PrimayValType.string): string这样的重载

标签: typescript typescript2.0 typescript1.8 typescript1.5


【解决方案1】:

[1]。建议:打字稿中的枚举用于定义命名常量。所以最好通过名称来限定字符串文字,例如

enum PrimayValType {
  STRING = 'string',
  BOOLEAN = 'boolean',
  INTEGER = 'integer',
  FLOAT   = 'float',
}

[2]。试试这个:

function f(b: Buffer, t: PrimayValType=PrimayValType.STRING): number | string | boolean {
  const value = b.toString('utf-8');
  switch (t) {
    case PrimayValType.BOOLEAN:
      const isBool = /true|false/.test(value);
      if (!isBool) {
        throw new TypeError(`${value} is invalid for a boolean type`);
      }
      return /true/.test(value);
    case PrimayValType.INTEGER:
    case PrimayValType.FLOAT:
      const isNaN = Number.isNaN(value);
      if (isNaN) {
        throw new TypeError(`${value} is invalid for a numeric type`);
      }
      return t === PrimayValType.INTEGER ? Number.parseInt(value) : Number.parseFloat(value);
    default:
      return value;
  }
}

essertEqual(f(Buffer.from("3.14", "utf-8"), PrimayValType.FLOAT), 3.14);
assertEqual(f(Buffer.from("3.14", "utf-8"), PrimayValType.INTEGER), 3);
assertTrue(f(Buffer.from("true", "utf-8")));

修改后的答案。打字稿方式。 (我没有测试过):


const parseBool = (value: string) => {
    if (!/true|false/.test(value)) throw new TypeError(`${value} is invalid for a boolean type`);
    return /true/.test(value);
};


type PrimType = "string" | "boolean" | "integer" | "float";
type Convert<T> = (str: string) => T;
type Convertors<T> = { [t in PrimType]: Convert<T> };

const convertors: Convertors<number|string|boolean> = {
    "integer": parseInt,
    "float": parseFloat,
    "boolean": parseBool,
    "string": (str) => str,
};

const f = (b: Buffer, t: PrimType) => convertors[t](b.toString('utf-8'));

【讨论】:

    【解决方案2】:

    您可以将函数的返回类型编写为映射类型。首先,将PrimaryValType 定义为从字符串文字到实际类型的映射:

    type PrimaryValType = {
        "string": string,
        "boolean": boolean,
        "integer": number,
        "float": number,
    }
    

    然后给定一个K extends keyof PrimaryValType类型的字符串,我们可以使用映射类型PrimaryValType[K]将它映射到正确的返回类型。

    要将输入解析为正确的类型,您可以打开类型字符串:

    function parse<K extends keyof PrimaryValType>(s: string, t: K): PrimaryValType[K] {
        switch (t) {
            case "integer": return parseInt(s) as PrimaryValType[K];
            case "float":   return parseFloat(s) as PrimaryValType[K];
            case "boolean": return (s === "true") as PrimaryValType[K];
            case "string":  s as PrimaryValType[K];
    
            default: throw new Error("Illegal t: " + t);
        }
    }
    

    类型断言是必需的,因为 Typescript 无法判断 t === 'integer'K 不能是 'string'。可以通过将解析器函数存储在对象中来整理代码,如@kmos.w 的回答:

    const parsers: { [K in keyof PrimaryValType]: (s: string) => PrimaryValType[K] } = {
        "integer": parseInt,
        "float":   parseFloat,
        "boolean": s => s === "true",
        "string":  s => s,
    };
    
    function parse<K extends keyof PrimaryValType>(s: string, t: K): PrimaryValType[K] {
        return parsers[t](s) as PrimaryValType[K];
    }
    

    出于同样的原因,仍然需要类型断言。

    Playground Link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      相关资源
      最近更新 更多