Home > Software design >  How to get type information from possibly undefined variable and type parameter
How to get type information from possibly undefined variable and type parameter

Time:03-08

I want to set a default value defVal based on the type of the incoming variable val. Many times the val will be undefined, so I can not use the JavaScript typeof operator. So T remains the only source of the type information. Of course the problem is that T is not known at runtime.

The defVal should be for example "" for string, 0 for number, etc... Like defVal = T is string ? "" : 0

function getValue<T>(val?: T): T
{
  const defVal = "FIXME" as unknown as T // <---- get default value based on T
  return val ?? defVal;
}

const s = getValue<string>();   // want s === ""
const n = getValue<number>(2);  // want n === 2

CodePudding user response:

Based on my own experience and the comment from @MikeS., I think that although the Typescript is great for many many things, this is the simple case that it can not add any useful value to the code, because it does not preserve the type information in the runtime.

So it is more useful to use "old school" approach with separately named methods.

function getValueString(val?: string): string
{
  return val ?? "";
}

function getValueNumber(val?: number): number
{
  return val ?? 0
}

const s = getValueString();
const n = getValueNumber(2);

console.log(s, n);

CodePudding user response:

TypeScript only exists at compile-time, so types can't be used to influence the runtime value that a function will return.

You can modify your function to use an overload signature, so that you can either infer from a value's type, or request a default type of value (taken from an object that you create). Here's an example of how to do that:

TS Playground

const defaults = {
  array: [] as unknown[],
  bigint: 0n,
  boolean: false,
  null: null,
  number: 0,
  object: {} as Record<PropertyKey, unknown>,
  string: '',
  undefined: undefined,
};

function getValue <T>(typeWanted: 'infer'): undefined;
function getValue <T>(typeWanted: 'infer', value: T): T;
function getValue <T extends keyof typeof defaults>(typeWanted: T): typeof defaults[T];
function getValue <T>(typeWanted: keyof typeof defaults | 'infer', value?: T) {
  if (typeWanted === 'infer') return value;
  return defaults[typeWanted];
}

console.log(getValue('array')); // unknown[]: []
console.log(getValue('bigint')); // bigint: 0n
console.log(getValue('boolean')); // boolean: false
console.log(getValue('null')); // null: null
console.log(getValue('number')); // number: 0
console.log(getValue('object')); // Record<PropertyKey, unknown>: {}
console.log(getValue('string')); // string: ""
console.log(getValue('undefined')); // undefined: undefined

console.log(getValue('infer')); // undefined
console.log(getValue('infer', 2)); // 2
console.log(getValue('infer', 'hello')); // "hello"
console.log(getValue('infer', ['hello', 'world'])); // string[]: ["hello", "world"]
console.log(getValue('infer', ['hello', 'world'] as const)); // readonly ["hello", "world"]: ["hello", "world"]
// ...etc.

  • Related