Home > Net >  generics in typescript with typeof for type guard
generics in typescript with typeof for type guard

Time:05-10

hi now iam learing typescript and i want to make a function to merge object or string and i use generic to add additional info to the function to can understand mergedObj.name after merge the two objects that's work fine if i deleted the condition for string but if i try to handle the two cases display error in name

Property 'name' does not exist on type 'string | ({ name: string; } & { age: number; })'. Property 'name' does not exist on type 'string'

i understand the mergedObj type here is

string | ({ name: string; } & { age: number; })

but how i can handle the two cases or multiple type guards with generics ?

type ObjectOrString = object | string;

function merge<T extends ObjectOrString , U extends ObjectOrString>(obj1: T, obj2: U) {
    if(typeof obj1 === 'string' || typeof obj2 === 'string'){
        return obj1.toString()   obj2.toString();
    }
    return Object.assign(obj1, obj2);
}


let mergedObj = merge({ name: "zeyad" }, { age: 22 }) ;

console.log(mergedObj.name); // Error Here 
/*  Property 'name' does not exist on type 'string | ({ name: string; } 
    & { age: number; })'.
    Property 'name' does not exist on type 'string'.ts(2339)
*/

let mergedStr = merge('zeyad', ' moamen');
console.log(mergedStr) // zeyad moamen

CodePudding user response:

You have to disambiguate the type somewhere, for example:

if (typeof mergedObj !== 'string')
    console.log(mergedObj.name); 

removes the error

CodePudding user response:

Using object type for representing hashmap data structure is not a best choise. It is better to use Record<string, unknown> instead. Since each value in JS is an object, type object is almost any type.

Your function has 4 allowed states:

type ObjectString = Record<string, unknown> | string


function merge<
  Obj1 extends string,
  Obj2 extends Record<string, unknown>
>(obj1: Obj1, obj2: Obj2): string
function merge<
  Obj1 extends Record<string, unknown>,
  Obj2 extends string
>(obj1: Obj1, obj2: Obj2): string
function merge<
  Obj1 extends string,
  Obj2 extends string
>(obj1: Obj1, obj2: Obj2): `${Obj1}${Obj2}`
function merge<
  Obj1 extends Record<string, unknown>,
  Obj2 extends Record<string, unknown>
>(obj1: Obj1, obj2: Obj2): Obj1 & Obj2
function merge(
  obj1: ObjectString,
  obj2: ObjectString,
): any {
  if (typeof obj1 === 'string' || typeof obj2 === 'string') {
    return obj1.toString   obj2.toString()
  }
  return Object.assign(obj1, obj2)
}

const a = merge('a', 'b') // 'ab'
const b = merge({ a: 1 }, { b: 2 }) // {a:number, b: number}
const c = merge({ a: 1 }, 'c') // string

Playground

For each state I have written an overload to make sure that return type is properly infered.

CodePudding user response:

It would better if you don't use general functions for several types. Because it adds additional unnecessary type checking and logic. Instead of it use differ functions for differ types. It is more simple, clear and safe

type Dict<T> = {
  [key: string]: T
}

function mergeObjects(obj1: Dict<string | number>, obj2: Dict<string | number>): Dict<string | number> {
    return Object.assign(obj1, obj2);
}

function mergeStrings(str1: string, str2: string): string {
    return str1   str2;
}

const mergedObj = mergeObjects({ name: "zeyad" }, { age: 22 }) ;
console.log(mergedObj.name);

const mergedStr = mergeStrings('zeyad', ' moamen');
console.log(mergedStr)

For example in your case I can pass to merge function object as first argument and string as second argument. Your function will run return Object.assign(obj1, obj2); and it will give you unexpected result:

const obj1 = {a: 'first'};
const obj2 = 'second';
const merge = Object.assign(obj1, obj2);

console.log(merge);
console.log(merge.constructor.name);

Or vice versa:

const obj1 = {a: 'first'};
const obj2 = 'second';
const merge = Object.assign(obj2, obj1);

console.log(merge);
console.log(merge.constructor.name);

The answers looks same, but they have different instance and it can be reason of unexpected errors in your application

  • Related