I'm just curious know if this is possible in TypeScript. Imagine I've a list on entity identifiers defined as an union type:
type EntityID = 'authors' | 'books' | 'programs';
Then, imagine we have the following classes for each of these entities:
class Author { getBirthday() }
class Book {}
class Program {}
Is there a way to make this work at type level so that given a concrete EntityID
TypeScript (and my IDE) know that the return type is a concrete entity class? Something like:
const book = genericGet('books', 10);
book.getBirthday() // This should fail at COMPILE TIME as it's an Author method
const author = genericGet('authors', 23);
author.getBirthday() // This is OK, as author is an Author
Thanks!
CodePudding user response:
You can create a mapping type from your enum to the types and then use a generic function (a factory method actually) to create the objects.
Something similar to this:
type EntityID = 'authors' | 'books' | 'programs';
type MapEntity = {
'authors': Author,
'books': Book,
'programs': Program
}
class Author {
getBirthday = ()=>{
}
}
class Book {}
class Program {}
function genericGet<T extends keyof MapEntity>(type: T, ...args: any[]): MapEntity[typeof type] {
switch (type) {
case "authors":
return new Author()
case "books":
return new Book() as any
case "programs":
return new Program() as any
default:
throw Error()
}
}
const book = genericGet('books', 10);
book.getBirthday() // This should fail at COMPILE TIME as it's an Author method
const author = genericGet('authors', 23);
author.getBirthday() // This is OK, as author is an Author
A couple of notes:
- You don't need to pass the generic type when using the factory method, as TS will infer it
- You need to specify a
default
clause for the switch even though there are no options there. This is so TS flow analysis is satisfied. - The type for the variables that call
genericGet
is properly infered
Here is playground to showcase this: