typeof
operator
const processValue = (value: string | number) => {
if (typeof value === 'string') {
// TypeScript knows 'value' is a string here
return value.toUpperCase();
} else {
// TypeScript knows 'value' is a number here
return value.toFixed(2);
}
}
Type Guards
interface User {
name: sring
role: string
}
interface Admin {
name: string
privs: string[]
}
const isAdmin(user: User | Admin): user is Admin {
return 'privs' in user
}
const processUser = (person: User | Admin) => {
if (isAdmin(person)) {
// TypeScript knows 'person' is an Admin here
console.log(person.privileges);
} else {
// TypeScript knows 'person' is a User here
console.log(person.role);
}
}
Descriminated unions (like the first example in the gist). Here's a pretty good example of a different one. "Rails Errors" - from: https://gist.github.com/t3dotgg/a486c4ae66d32bf17c09c73609dacc5b
type Success<T> = {
data: T
error: null
}
type Failure<E> = {
data: null
error: E
}
// if the object property types match success, return a success, otherwise return a failure type
type Result<T, E = Error> = Success<T> | Failure<E>
const tryCatch = async <T, E = Error> (
promise: Promise<T>
): Promise<Result<T, E>> {
try {
const { data } = await promise
return { data, error: null }
} catch (error) {
return { data: null, error: error as E}
}
}
function hasProperty<T, K extends string>(
obj: T,
prop: K
): obj is T & Record<K, unknown> {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function processObject(obj: unknown) {
if (hasProperty(obj, 'name')) {
// TypeScript knows obj has a 'name' property
console.log(obj.name);
}
}
And others...