Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Key of union type as function argument

I’m trying to implement a UI where a client can search using either of many attributes. AFAIK this could be achieved with union types. So far my implementation looks like this

interface GivenNameSearchTerm {
    readonly givenName: string;
}

interface FamilyNameSearchTerm {
    readonly familyName: string;
}

type SearchTerm = GivenNameSearchTerm | FamilyNameSearchTerm;

const search = (input: SearchTerm): Results[] => {
  // do stuff
}

Now, I want to call the search function by allowing user to select both which attribute to search by and its value.

According to this other question, narrowing the possible keys can be done with conditional types

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

type SearchInput<T> = T extends T ? keyof T : never;

const test = (key: SearchInput<SearchTerm>, value: string) => {
    search({ [key]: value });
}

This approach lets me narrow the values key can have, allowing only givenName or familyName, but TypeScript shows this error:

TS2345: Argument of type  { [x: string]: string; }  is not assignable to parameter of type  SearchTerm 
Property  familyName  is missing in type  { [x: string]: string; }  but required in type  FamilyNameSearchTerm

If I hardcode the key everything works, but user should be able to choose it. Any ideas?

I checked the documentation on narrowing but in this case I don’t need to narrow the type, just accept any of the union ones. Though by narrowing the type there are no errors but this leads to duplicate code:

const test = (key: SearchInput<SearchTerm>, value: string) => {
  switch (key) {
    case 'givenName':
      search({ [key]: value });
      break;
    case 'familyName':
      search({ [key]: value });
      break;
  }
}

>Solution :

You can cast the type using:

search({ [key]: value } as {[key in typeof key]: typeof value});

interface GivenNameSearchTerm {
    readonly givenName: string;
}

interface FamilyNameSearchTerm {
    readonly familyName: string;
}

type SearchTerm = GivenNameSearchTerm | FamilyNameSearchTerm;

const search = (input: SearchTerm): Results[] => {
// do stuff
return "";
}

type SearchInput<T> = T extends T ? keyof T : never;

const test = (key: SearchInput<SearchTerm>, value: string) => {
    search({ [key]: value } as {[key in typeof key]: typeof value});
}

test("inValid", "asd"); // TS error: Argument of type '"inValid"' is not assignable to parameter of type '"givenName" | "familyName"'.
test("givenName", "asd");  // Pass
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading