Say that you have this type:
type List = (Subject | null | undefined)[]
Perhaps it is the result of querying for a list of identifications, where not every entry is guaranteed to have a result.
Nothing strange so far. However, let's say you now want to remove null
and undefined
elements, in order to do a further operation on the leftover Subject
elements.
interface Subject {
id: string | number
name: string
}
type List = (Subject | null | undefined)[]
const joe: Subject = { id: 0, name: 'joe' }
const jane: Subject = { id: 10, name: 'jane' }
const list: List = [joe, null, undefined, jane]
const names = list.filter(entry => !!entry).map(entry => entry.name)
console.log(names)
Unfortunately this does not work:
TSError: β¨― Unable to compile TypeScript:
index.ts:12:58 - error TS2533: Object is possibly 'null' or 'undefined'.
We need to tell TypeScript that the filter operation ensures only Subject
types are left.
function exists<T>(value: T | null | undefined): value is T {
return value === (value ?? !value)
}
The exists
function, for example, makes sure to assert that value is of type T
.
In production code, try to avoid generic naming such as
T
To filter and have Subject
types as leftover:
interface Subject {
id: string | number
name: string
}
type List = (Subject | null | undefined)[]
const joe: Subject = { id: 0, name: 'joe' }
const jane: Subject = { id: 10, name: 'jane' }
const list: List = [joe, null, undefined, jane]
function exists<T>(value: T | null | undefined): value is T {
return value === (value ?? !value)
}
const names = list.filter(exists).map(entry => entry.name)
console.log(names) // [ "joe", "jane" ]
That's all! Now TypeScript
makes sure that name
is available on the filtered items.
Type guards are fun!
Top comments (1)
Vey cool and easy to understand thanks for sharing.