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
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