简体   繁体   中英

get generic type in TypeScript

I need a function to return some of "default" values for a type. Say, I want -1 for integers, "none" for strings, MyEnum.DefaultValue for an enum...

I started from the following problem to implement a generic function for the code bellow: 在此处输入图像描述

( .value is of 'string' type that I try to convert to strings, numbers or booleans)

public getPropertyValue<Type>(propertyCode: string, properties: PropertyValue[]): Type {
  return <Type>(properties.find(x => x.propertyCode === propertyCode)?.value || defaultValue);
}

public async calculate(properties: PropertyValue[]): Promise<CalculatorResult[]> {
  const STA = <EnumSTA>(properties.find(x => x.propertyCode === 'STA')?.value || EnumSTA.NF_EN_1992_1_1_NA);
  const FYK = <number>(properties.find(x => x.propertyCode === 'FYK')?.value || 0);
  const CON = <EnumCON>(properties.find(x => x.propertyCode === 'CON')?.value || EnumCON.C3037);
  const BCO = <number>(properties.find(x => x.propertyCode === 'BCO')?.value || 0);
  const HCC = <string>(properties.find(x => x.propertyCode === 'HCC')?.value || 'none');
  const GTC = <number>(properties.find(x => x.propertyCode === 'GTC')?.value || 0);

I want to write a generic function that would return me the default type value, like this

Some pseudo code bellow:

public getDefaultValue<Type>(): Type {
  switch(typeof (Type)){
    case 'boolean': return false;
    case 'number': return -1;
    case 'string': return "none";
    case 'EnumCity': return EnumCity.Paris;
  }
}

what closest real variant of that function is possible with Type/Java/Script?

PS.

Suppose I have the full list of possible types, so, I could create something like this, that compiles well in TypeScript:

getPropertyValue<Type extends (string | number | boolean | EnumCity)>()
{
   return <Type>(properties.find(x => x.propertyCode === propertyCode)?.value);
}

TypeScript is transpiled into JavaScript, and JavaScript will not know about the T in getPropertyValue<T> , since all type annotations are erased from your TS code when it is transpiled into JS.

I'd suggest you rewrite your getPropertyValue to accept another parameter that will be used as the default value when needed. Much like Java's getOrDefault convention or Rust's unwrap_or .

public getPropertyValueOrDefault<ValueType, DefaultType = ValueType>(
    propertyCode: string,
    properties: PropertyValue[],
    defaultValue: DefaultType
): ValueType | DefaultType {
    return (
        properties.find(
            x => x.propertyCode === propertyCode
        )?.value as ValueType ?? defaultValue
    );
}

Notice that the property.value as ValueType is a footgun . If I understood your question correctly, property.value is actually always a string . Then the only type safe option is to have different getter functions for different types:

public getBooleanPropertyOrDefault(
    propertyCode: string,
    properties: PropertyValue[],
    defaultValue: boolean
): Boolean {
    const value = properties.find(x => x.propertyCode === propertyCode);
    return typeof value === "undefined" ? defaultValue : Boolean(value);
}

public getNumberPropertyOrDefault(
    propertyCode: string,
    properties: PropertyValue[],
    defaultValue: number
): Number {
    const value = properties.find(x => x.propertyCode === propertyCode).value;
    return typeof value === "undefined" ? defaultValue : Number(value);
}

// And so on

Or even something like my preferred solution to avoid the cognitive load with long function names:

public getProp(code: string, props: PropertyValue[]) {
    const value = props.find(x => x.propertyCode === propertyCode).value;
    return {
        asBoolOr: (dflt: boolean) => Boolean(value ?? dflt),
        asNumberOr: (dflt: number) => Number(value ?? dflt),
        asStringOr: (dflt: string) => String(value ?? dflt),
        parseOr: <T>(parser: (value: string) => T, dflt: T) =>
            typeof value !== "undefined"
                ? parser(value)
                : dflt,
    };
}

Which you'd then use as

const STA = getProp("STA", properties)
    .parseOr(/* parseSta */, EnumSTA.NF_EN_1992_1_1_NA);
const FYK = getProp("FYK", properties).asNumberOr(0);

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