Kuba Jagoda
Kuba Jagoda

Reputation: 5547

Is there a way to "extract" the type of TypeScript interface property?

Let's suppose there's a typing file for library X which includes some interfaces.

interface I1 {
    x: any;
}
    
interface I2 {
    y: {
        a: I1,
        b: I1,
        c: I1
    }
    z: any
}

In order to work with this library I need pass around an object that is of exactly the same type as I2.y. I can of course create identical interface in my source files:

interface MyInterface {
    a: I1,
    b: I1,
    c: I1
}

let myVar: MyInterface;

but then I get the burden of keeping it up to date with the one from library, moreover it can be very large and result in lot of code duplication.

Therefore, is there any way to "extract" the type of this specific property of the interface? Something similar to let myVar: typeof I2.y (which doesn't work and results in "Cannot find name I2" error).


Edit: after playing a bit in TS Playground I noticed that following code achieves exactly what I want to:

declare var x: I2;
let y: typeof x.y;

However it requires a redundant variable x to be declared. I am looking for a way to achieve this without that declaration.

Upvotes: 370

Views: 218825

Answers (6)

WestMountain
WestMountain

Reputation: 625

const foo = ()=>{
   return {name: "test", age: 5}
}
type T1 = ReturnType<typeof foo> // {name: string, age: number}
type T2 = ReturnType<typeof foo>['name'] // string
type T3 = T1['age'] // number

Upvotes: -1

Michał Miszczyszyn
Michał Miszczyszyn

Reputation: 12701

It wasn't possible before but luckily it is now, since TypeScript version 2.1. It was released on the 7th of December 2016 and introduces indexed access types, also called lookup types.

The syntax looks like element access but is written in place of types. So in your case:

interface I1 {
    x: any;
}

interface I2 {
    y: {
        a: I1,
        b: I1,
        c: I1
    }
    z: any
}

let myVar: I2['y'];  // indexed access type

Now myVar has the type of I2.y.

Check it out in TypeScript Playground.

Upvotes: 657

Gabriel Petersson
Gabriel Petersson

Reputation: 10412

keyof Colors will return a list of all keys "white" | "black". When this list of keys gets passed to the Colors interface, the type will be all the values of the given keys, "#fff" | #000.

interface Colors {
  white: "#fff"
  black: "#000"
}

type ColorValues = Colors[keyof Colors]
// ColorValues = "#fff" | "#000"

Upvotes: 24

James Bond
James Bond

Reputation: 2966

Just an example of extracting a literal type from the union object type:

type Config = {
    key: "start_time",
    value: number,
} | {
    key: "currency",
    value: string,
}

export type ConfigKey = Config["key"];
// "start_time"|"currency"

Upvotes: 25

Ben Winding
Ben Winding

Reputation: 11787

To expand on the accepted answer, you can also assign the type using the type keyword and use it in other places.

// Some obscure library
interface A {
  prop: {
    name: string;
    age: number;
  }
}

// Your helper type
type A_Prop = A['prop']

// Usage
const myThing: A_prop = { name: 'June', age: 29 };

Upvotes: 38

Noone
Noone

Reputation: 425

An interface is like the definition of an object. Then y is a property of your I2 object, that is of a certain type, in that case "anonymous".

You could use another interface to define y and then use it as your y type like this

interface ytype {
   a: I1;
   b: I1;
   c: I1;
}

interface I2 {
    y: ytype;
    z: any;
}

You can put your interface in a file and use extract so you can import it in other files of your projects

export interface ytype {
   a: I1;
   b: I1;
   c: I1;
}



 export interface I2 {
        y: ytype;
        z: any;
    }

You can import it that way :

   import {I1, I2, ytype} from 'your_file'

Upvotes: -5

Related Questions