Reputation: 2747
How does one narrow a single property on an object?
type Foo = {
a: number | undefined
b: string
}
let foo: Foo
foo = {
a: 1,
b: 'b'
}
// function accepts only narrowed version of Foo
const fn = (arg: Foo & { a: number })=>console.log(arg.a)
if(foo.a!==undefined){
// compiler should know foo's type is now { a: number, b: string }
fn(foo)
}
One solution would be to manual override the type as follows:
fn(foo as Foo & { a: number })
However is there a better way?
Upvotes: 1
Views: 52
Reputation: 532
Typescript does not narrow object types based on conditional checks on object properties. You could achieve this by creating a user-defined type guard that checks on typeof Foo.a
like following:
type Foo = {
a: number | undefined
b: string
}
//custom type guard using type predicate
function fooHasA(obj: Foo): obj is Foo & {
a: Exclude<Foo["a"], undefined>
} {
return typeof obj.a !== 'undefined';
}
let foo: Foo
foo = {
a: 1,
b: 'b'
}
// function accepts only narrowed version of Foo
const fn = (arg: Foo & { a: number })=>console.log(arg.a)
if(fooHasA(foo)){
// compiler knows now that foo's type is now { a: number, b: string }
fn(foo)
}
Also, you don't need to create new types just for the sake of narrowing an object.
Upvotes: 1
Reputation: 165586
Since you only need a
the obvious thing to do is to redefine your function to take a number.
const fn = (arg: number)=>console.log(arg)
if(foo.a){
fn(foo.a)
}
Another option is to use a type predicate.
// define a new type just to make things tidy
type FooWithA = Foo & { a:number }
function isFooWithA(obj: Foo): obj is FooWithA {
return obj.a !== undefined;
}
const fn = (arg: FooWithA)=>console.log(arg.a)
if(isFooWithA(foo)) {
fn(foo);
}
What might be best is to rethink your types to eliminate the optional parameter.
type A = {
a: number
}
type B = {
b: string
}
type AB = A & B
const fn = (arg: A)=>console.log(arg.a)
const foo: AB = {
a: 1,
b: "two"
};
fn(foo)
A more detailed example is necessary to know which you should use.
Upvotes: 1