Reputation: 6894
Let's say I have this function (try online):
type Person = { name: string }
function getName(p: ?Person): ?string {
if (!p) {
return p
} else {
return p.name
}
}
const frank: Person = { name: 'Frank' }
/*
* In order to work now, this has to have a maybe type
* const name: ?string = getName(frank)
* Even though I know it will is guaranteed to be a string
*/
const name: string = getName(frank)
I get an error, because Flow thinks that getName
could return undefined
. Though this happens only, if p
would be undefined, which clearly is not the case.
Can I somehow type the function in a way, that the return type is undefined
ONLY when the parameter is undefined
and otherwise a string?
Edit:
Clarifying Example: I have an easy function:
function getName(person) {
if (! person) {
return undefined
} else {
return person.name
}
}
const maybeFrank: ?Person = getFrank()
const definitelyMaria: Person = getMaria()
const frankName: ?string = getName(maybeFrank)
const mariaName: string = getName(definitelyMaria)
I would like to type it in a way, so:
Is that possible?
Upvotes: 2
Views: 1605
Reputation: 1444
I liked the solution by @loganfsmyth, but found it to involve too much typing for each selector.
I came up with a way to make this more generic, which saves you from creating multiple type signatures for each selector.
type FinderT<A, B, C> = (A, B) => C
declare function maybe<B, C>(cb: FinderT<number, B, C>, id: number, other: B): C
declare function maybe<B, C>(cb: FinderT<number, B, C>, id: ?number, other: B): void // eslint-disable-line no-redeclare
export function maybe(cb, id, other) { // eslint-disable-line no-redeclare
if (!id) {
return undefined
} else {
return cb(id, other)
}
}
Usage:
maybe(findPerson, 42, state)
This correctly returns a ?Thing
or Thing
based on whether the id
is a number
or a ?number
. Currently it only handles one extra argument, but this could probably be expanded to include more arguments.
Upvotes: 0
Reputation: 161457
My opinion would be to remove the ?
from the function argument and leave the disambiguation up to the call site. e.g.
type Person = { name: string }
function getName(person: Person): string {
return person.name
}
const maybeFrank: ?Person = Math.random() > 0.5 ? {name: 'Frank'} : undefined;
const definitelyMaria: Person = {name: 'Maria'};
const frankName: ?string = maybeFrank && getName(maybeFrank)
const mariaName: string = getName(definitelyMaria);
otherwise, if you really want to, you can explicitly redeclare the function with explicit types
type Person = { name: string }
declare function getName(p: Person): string;
declare function getName(p: void): void;
function getName(p: ?Person): ?string {
if (!p) {
return undefined
} else {
return p.name
}
}
const frank: Person = { name: 'Frank' }
const name: string = getName(frank)
Upvotes: 3