I struggle to make typescript understand that I'm already handling the undefined cases of one property (project
) in this (shortened) example:
interface Entry {
id: number
project?: Project
isPublished?: boolean
}
entries
.filter(entry => !!entry.project)
.map(entry => ({
title: entry.project.title
}))
(entries
is an array of Entry
s here) and the compiler complains on the line of entry.project.title
on the project
property that TS2532: Object is possibly 'undefined'
even though I have the filter removing the undefined ones. The other optional property is still optional after the filtering, I thus cannot use Required
interface :/
How can I make TS understand the property is definitely defined in the map function?
Thank you
-- Edit: added another optional property / clarification as response to a suggested answer that would make all properties required
CodePudding user response:
You need to define filter
callback as a typeguard and use Required
type util to make all interface props required:
type Project = {
title: string
}
interface Entry {
id: number
project?: Project
}
type WithProject = Required<Entry>
declare const entries: Entry[]
entries
.filter((entry): entry is WithProject => !!entry.project)
.map(entry => ({
title: entry.project.title
}))
If you are interested in more type utils please see docs
CodePudding user response:
Adding onto @captain-yossarian's answer and addressing OP's comment on it, we can make our own utility type to require only a few specific keys:
type Project = {
title: string
}
interface Entry {
id: number
project?: Project
}
type SomeAreRequired<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>;
type WithProject = SomeAreRequired<Entry, "project">;
declare const entries: Entry[]
entries
.filter((entry): entry is WithProject => !!entry.project)
.map(entry => ({
title: entry.project.title
}))
SomeAreRequired
picks all keys in T
and makes them required, then adds it to T
with the K
keys omitted, effectively making only the selected keys required.