Home > database >  How to create a generic function that supports two different types
How to create a generic function that supports two different types

Time:12-28

I want to create a generic function that supports 2 different types, those types are not used interchangeably only one at time yet the function will serve as helper function to be using in both types.

type  userType = {id : string; user : string}
type  adminType =  {id : string; admin : string}


const copmairByLoop = (firstObject: userType | adminType, secondObject: userType | adminType) => {
for (const key in firstObject) {
if (firstObject[key as keyof userType | adminType] !== secondObject[key as keyof userType | adminType]) {
alert('are not the same');
}
}
};


// use case examples 
const objectUser1 : userType = {id : '234234'; user : 'jack'}
const objectUser2 : userType ={id : '454366546'; user : 'max'}
// copmairByLoop(objectTest1,objectTest2) users are not the same...

// use case examples 
const objectAdmin1 : adminType = {id : '44433'; admin: 'joe'}
const objectAdmin2 : adminType ={id : '7778'; admin: 'alfred'}
// copmairByLoop(objectAdmin1,objectAdmin2) admins are not the same...




// my current refactor 
const copmairByLoop = <T, U>(firstObject: T | U, secondObject:  T | U): never => {
for (const key in firstObject) {
if (firstObject[key as keyof T | U] !== secondObject[key as keyof T | U]) {
alert('are not the same');
}
}
};

Please check the sandbox

Codesandbox

CodePudding user response:

After the discussion in the comments, I think I understand what you want to do. My interpretation is that you want to have a single function that returns whether or not two users/admins are the same. The typical way of doing this would be to make a single function that takes a union of userType and adminType, like this:

type User = {
  id: string
  user: string
}

type Admin = {
  id: string
  admin: string
}

function IsEqual(a: User | Admin, b: User | Admin): boolean {
  return a.id === b.id
}

The downside is that this works by constraining the available properties to those that exist on both types (in this case id, but presumably each entry would have a unique id property anyway). It also means you can't check that they're both the same type, unless you add a usertype property to the types or something such.

The other approach is to use function overloading:

function IsEqual(a: User, b: User): boolean;
function IsEqual(a: Admin, b: Admin): boolean;

function IsEqual(_a: unknown, _b: unknown): boolean {
  const a = _a as User & Admin;
  const b = _b as User & Admin;
  // now both a and b have all three fields;
  // any that were not there before have the
  // value "undefined"
  
  // if comparing two Users, admin will be undefined on both,
  // and undefined == undefined evaluates to true, so that
  // gives the expected results
  return a.admin === b.admin && a.id === b.id && a.user == b.user;
}

const userA = {id: "a", user: "a"}
const userB = {id: "b", user: "b"}

const adminA = {id: "a", admin: "a"}
const adminB = {id: "b", admin: "b"}

IsEqual(userA, userB) // works; returns false
IsEqual(userA, adminA) // compiler error
IsEqual(adminA, adminB) // works; returns false

Note that unlike many other languages that support function overloading, Typescript only supports a single implementation body, so you need to manually wrangle the inputs to make them comparable.

Furthermore, I strongly recommend against looping over all properties with a for (const key in obj) loop, because there are no protections (either at compile-time or run-time) against the input objects having more properties than the ones specified by the type. If we do this, we get unexpected results:

function IsEqual(_a: unknown, _b: unknown): boolean {
  const a = _a as User & Admin;
  const b = _b as User & Admin;
  
  for (const key in a) {
    if (a[key as keyof User] !== b[key as keyof User]) return false
  }

  return true
}

const user1 = {id: "a", user: "a",}
const user2 = {id: "a", user: "a", foo: "whee"}

IsEqual(user1, user2) // works; returns true
IsEqual(user2, user1) // works; returns false
  • Related