MoreThanTom
MoreThanTom

Reputation: 526

How to narrow types using a property

Is there any way to define getInterface in this context so that res has the type number?

To clarify: I am not trying to write these methods, I have an enviroment where a method exists which returns different objects depending on parameters.type, and I'm trying to find a way to type them

interface A {
  tag: 'a'
  do_one: () => number;
}
interface B {
  tag: 'b'
  do_one: () => string;
}
type Interface = A | B
let one = getInterface({ type: 'a' })
let res = one.do_one()

Upvotes: 1

Views: 128

Answers (5)

MoreThanTom
MoreThanTom

Reputation: 526

Thankyou for all the help! I admit my question was a bit of a mess, I definitely should've been clearer.

Anyway, I managed to get the correct typing -

type FilterTag<T, U> = T extends { tag: U } ? T : never
type Interface<T> = FilterTag<A | B, T>
declare function getInterface<T extends string>(params: { type: T }): Interface<T>

which means that getInterface({ type 'X' }) will always return the right interface, and there is only one union type to edit whenever it needs to be changed

Upvotes: 1

Nathan Xabedi
Nathan Xabedi

Reputation: 1127

declare function getInterface(arg: { type: 'a' }): A;
declare function getInterface(arg: { type: 'b' }): B;

let one = getInterface({ type: 'a'} )
let res = one.do_one()  // res is a number

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

While your question is not 100% clear, in my reading you what getInterface to take the type and return the apropriate value based on the type.

You can do this using overloads or conditional types:

With overloads:

type Interface = A | B

function getInterface(v: { type: 'a' }): A
function getInterface(v: { type: 'b'}): B 
function getInterface(v: { type: 'b'} | { type: 'a'}): A | B {
    return null!
}
let one = getInterface({ type: 'a' })
let res = one.do_one()

With conditional types

interface A {
    tag: 'a'
    do_one: () => number;
}
interface B {
    tag: 'b'
    do_one: () => string;
}
type Interface = A | B

type GetJustTypes = Interface extends infer I ? I extends { tag: infer U } ? { tag: U } : never : never
function getInterface<T extends GetJustTypes>(v: T): Extract<Interface, T>
function getInterface(v: { tag: 'b' } | { tag: 'a' }): A | B {
    return null!
}
let one = getInterface({ tag: 'a' })
let res = one.do_one()

Upvotes: 1

Mukundhan
Mukundhan

Reputation: 3457

We need to understand typings clearer, typings are present to avoid errors during compilation time. after compilation, the end result will be pure javascript.

As for your question, typing can be added only using or | condition which will be checked during compilation only.

Dynamic typing is a myth yet to be solved.

But there is a method instance-of which can be used.

Upvotes: 0

manish kumar
manish kumar

Reputation: 4692

It has to be dealt with do_one() function basically

getInterface(<someparam>):number{
   do_one():number{
      return 4;
   }
}
let res:number = one.do_one();

Upvotes: 0

Related Questions