Advanced Typescript Questions - Conditional Types
In this post, we will go through some advanced Typescript Type concepts.
Hello, this is the fourth of my Typescript interview series. I also wrote a post of beginner questions, and two posts for intermediate questions - one, and two.
Q1 - Conditional Types
Given the following code
type Car = {
model: string
}
type House = {
sqft: number
}
function houseOrCar<K extends 'Car' | 'House'>(s: K): Car | House {
return s === 'Car' ? { model: 'hyundai' } : { sqft: 100_000 } as any
}When we try to use the houseOrCar() function, the resulting type is Car | House as shown:
const car = houseOrCar('Car') // Car | House
const house = houseOrCar('House') // Car | HouseHow can we modify the type definitions such that the function houseOrCar() returns an object of type Car when input is 'Car' and House when the input is 'House'?
/* DEFINE A TYPE HERE */
function houseOrCar<K extends 'Car' | 'House'>: /* CHANGE CODE HERE */ {
return s === 'Car' ? { model: 'hyundai' } : { sqft: 100_000 } as any
}
const car = houseOrCar('Car') // Car
const house = houseOrCar('House') // HouseAnswer
Use conditional types utility type.
type StringToType<T> = T extends 'Car' ? Car : T extends 'House' ? House : never
function houseOrCar<K extends 'Car' | 'House'>(s: K): StringToType<K> { return s === 'Car' ? { model: 'hyundai' } : { sqft: 100_000 } as any
}Q2 never filter
Given the following code
type Employee = {
name: string,
age: number,
weaknesses: never
}How can we extract all the possible different types of possible value-types except never?
type PossibleTypes = /* YOUR CODE HERE * /
// type PossibleTypes = string | numberAnswer
Typescript removes never whenever it has a union, so a simple keyof operation will suffice.
type PossibleTypes = Employee[keyof Employee] // string | numberQ3 How to get properties of a specific type
Given the following code
type House = {
sqft: number,
address: string,
floors: number,
bedrooms: number,
builtAt: Date
}We want to create a generic type such that we can extract properties of a specific type, as shown:
type NumberProperties = PropsOfType<House, number> // "sqft" | "floors" | "bedrooms"
type StringProperties = PropsOfType<House, string> // "address"
type DateProperties = PropsOfType<House, Date> // "builtAt"Write a generic type PropsOfType that can achieve the above.
Hint: the answers from the previous two questions above will help
Hint 1
Here is what the following code gets me.
type PropsOfTypeNumber = {
[K in keyof House]: House[K] extends number ? K : never
}
// type PropsOfTypeNumber = {
// sqft: "sqft";
// address: never;
// floors: "floors";
// bedrooms: "bedrooms";
// builtAt: never;
// }We have a type where the keys are the same but the values are now the string representation of the key IF they are a number. 🤔 Otherwise they are never. If only we could filter out those nevers…
And then make it generic.
Hint 2
Here is what the following code gets me.
type ValueOf<T> = T[keyof T]
type PropsOfTypeNumber = ValueOf<{
[K in keyof House]: House[K] extends number ? K : never
}>
// type PropsOfTypeNumber = "sqft" | "floors" | "bedroomsWe are now able to successfully filter out the nevers, as they are filtered out by Typescript unions.
Now we just need to make it generic… 🤔
Answer
type ValueOf<T> = T[keyof T]
type PropsOfType<T, P> = ValueOf<{
[K in keyof T]: T[K] extends P ? K : never
}>type NumberProperties = PropsOfType<House, number> // "sqft" | "floors" | "bedrooms"
type StringProperties = PropsOfType<House, string> // "address"
type DateProperties = PropsOfType<House, Date> // "builtAt"Great job! Hope you learned something from this.
Happy learning!