Home > Mobile >  how to create a dynamic scalable typeExclude helper function that supports many different types with
how to create a dynamic scalable typeExclude helper function that supports many different types with

Time:12-22

I am trying to create a dynamic function that check if a certain element fullfils some criteria as an extension to this question.

My current issue is that the key string becomes unavailable as key since there is no signature. How do I make the key dynamic for all incoming types?

type TypeA = { id: string; equipment: string; weight: string }
type TypeB = { id: string; equipment: string; material: string }
type TypeC = { id: string; equipment: string; width: string } 
type TypeD = { id: string; equipment: string; old: string } 
type TypeE = { id: string; equipment: string; recent: string }
type TypeF = { id: string; equipment: string; broken: string }

type AllTypes = (TypeA | TypeB | TypeC| TypeD | TypeE | TypeF )[]
const items : AllTypes = [{ id: '11245', equipment: 'hammer', recent: 'yes' }, { id: '11335', equipment: 'screwdriver', material: 'metal' }]


const typeExclude = (key: string, el: TypeA | TypeB | TypeC| TypeD | TypeE | TypeF) => {
       return key in el ? el[key as any /*the current type at hand*/] : undefined;
};

const arr = items.map(el => typeExclude('material', el) ? 'valid':'invalid')

console.log(arr)

CodePudding user response:

I think the problem you are facing is due to the fact that in operator type narrowing works for string literals, but not for variables. This is also discussed in the answers to this question.

The solution is to implement your own type guard which asserts that the value of the key variable is an actual key of the object in question.

type TypeA = { id: string; equipment: string; weight: string }
type TypeB = { id: string; equipment: string; material: string }
type TypeC = { id: string; equipment: string; width: string } 
type TypeD = { id: string; equipment: string; old: string } 
type TypeE = { id: string; equipment: string; recent: string }
type TypeF = { id: string; equipment: string; broken: string }

type Type = TypeA | TypeB | TypeC | TypeD | TypeE | TypeF;
type Key<U> = U extends U ? keyof U : never;

const items: Type[] = [
    { id: '11245', equipment: 'hammer', recent: 'yes' },
    { id: '11335', equipment: 'screwdriver', material: 'metal' }
];

const hasKey =
    <T extends object>(o: T, key: PropertyKey): key is keyof T => key in o;

const typeExclude =
    (key: Key<Type>, el: Type) => hasKey(el, key) ? el[key] : undefined

const arr = items.map(el => typeExclude('material', el) ? 'valid' : 'invalid');

console.log(arr);

Playground link

In addition, I created a distributive conditional type Key<U> that creates a union of the keys of all types that are part of the union U. This restricts the key parameter of your typeExclude() function to only accept known keys.

CodePudding user response:

I saw already solved this issue but i think this below code little bit cleaner and less code with my try using interface.

interface IType {
  id: string;
  equipment: string;
  weight?: string;
  material?: string;
  width?: string;
  old?: string;
  recent?: string;
  broken?: string;
}

const items: IType[] = [
  { id: "11245", equipment: "hammer", recent: "yes" },
  { id: "11335", equipment: "screwdriver", material: "metal" },
];

const typeExclude = <T extends keyof IType>(key: T, obj: IType) => {
  return key in obj ? obj[key] : undefined;
};

const arr = items.map((el) =>
  typeExclude("material", el) ? "valid" : "invalid"
);

console.log(arr); // ['invalid', 'valid']

  • Related