132

As stated in the documentation of TypeScript about the keyof operator, one can get a property of an object instance using the function below.

function getProperty<T, K extends keyof T>(o: T, name: K) {
    return o[name];
}

Of course, one can get the type of the property by replacing return o[name] with return typeof o[name]. Is there a way to retrieve the type of the property without passing any object instance?

function getPropertyType<T>(name: keyof T) {
    // something like T[name]?
}
2
  • 2
    According to TypeScript's no goals number 5: "Add or rely on run-time type information in programs, or emit different code based on the results of the type" I'm afraid this might not be possible as it sounds like type reflection to me what you are trying to do. github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals Commented Aug 26, 2017 at 13:07
  • 2
    @k0pernikus I don't think this question requires any thing related to runtime information or different code compilation due to types. It's all static typing. Commented Jul 28, 2021 at 8:39

5 Answers 5

188

Yes, lookup types work just fine:

type BarType = FooType['bar'];

It expects in this case that FooType is an object like:

type FooType = {
    bar: string;
}

It sets BarType to the same type as FooType['bar'], so to a string.

PS: FooType can also be an interface or class.

Sign up to request clarification or add additional context in comments.

3 Comments

Note that if using eslint, you'll want to make sure you're using the "new" @babel/eslint-parser library to avoid syntax errors. More details in this github issue.
If you're having trouble because FooType can be null or undefined, use NonNullable. type BarType = NonNullable<FooType>['bar']
Just a note to create a type which is the union of all the properties type Properties<T extends Object> = T[keyof T]
129

Is this what you're looking for?

type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];

and get type of an object property by doing:

type MyPropType = PropType<ObjType, '<key>'>;

which is the same as the way of using Pick in typescript, and it can report compile error if there's any invalid key passed in.

Updates

As @astoilkov suggested, a simpler alternative is PropType['key'].

6 Comments

A simpler solution is ObjType['key']. No need for defining an extra type and the syntax is easier to read.
Thanks, I preferred the shorthand e.g. private connection: Database['connection'];.
@astoilko, your solution has changed my life. 🎩
I don't see PropType in the typescript spec. Seems like it's just a react concept??
@apoteet PropType is just a user defined generic type. It can be named anything.
|
31

Of course, one can get the type of the property by replacing return o[name] into return typeof o[name].

Not really... if you did that:

function getTypeofProperty<T, K extends keyof T>(o: T, name: K) {
    return typeof o[name];
}

You would get a return value of the type "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function", because you're using the JavaScript typeof operator at runtime, which returns a string like "object", not the compile-time type seen by TypeScript.

TypeScript also uses the keyword typeof as the compile-time type query operator. You can tell the difference by looking at the places where it appears. The type query typeof only appears in places where you are writing a type. For example:

const a = { foo: 0 };
const b: Array<typeof a> = [{ foo: 1 }, { foo: 2 }, {foo: 3}]; 
const c: string = typeof a; // "object"

In b, you can see that typeof a appears where you would write a type expression, so it is a TypeScript type query, and Array<typeof a> is evaluated as Array<{foo: number}>. On the other hand, in c, typeof a appears where you would write an value expression, so it is the JavaScript typeof operator, and will evaluate to the string "object" at runtime.


As @k0pernikus mentioned, TypeScript doesn't (and doesn't intend to) allow you to get compile-time type information at runtime. So there is no typeof operator which acts at runtime and returns compile-time information.


Of course, if you want type information about a property at compile time, you can do that, using what's called lookup types. Let's examine the return value of that getProperty() function:

function getProperty<T, K extends keyof T>(o: T, name: K) {
    return  o[name];
} // hover over getProperty to see that the return value is of type T[K]

That T[K] in a type position means "the type of property with a key of type K on an object of type T". Since this is a type-level operation, you can do it at compile-time without declaring any values of the type T or K. For example,

type RegExpFlags = RegExp['flags']; // RegExpFlags is string
const flags: RegExpFlags = 'ig';

Here, you are looking up the "flags" key of the type of RegExp objects and getting back the string type, and you can declare a value of type RegExp['flags'] without having a value of type RegExp anywhere.


That's the closest I can come to answering your question without more information about what you need. Hope that helps. Good luck!

4 Comments

hello, thank you for the answer, it seems a bit hard to understand for me; could you please give an example of how to get the type of a class property (for example string) at compile time ? Thank you
Does the documentation about lookup types (also called "index access types") help? If you have class Foo { bar: string = "baz" } and then type FooBar = Foo["bar"], the type FooBar will be string.
that documentation for lookup types was pretty abstract, what really is "type Foobar" ? what i needed was a string describing the type, like "String" or "Number"; in the end what worked was blog.wolksoftware.com/…
You know what, thinking in terms of a "type-level" operation as you put it, has really cleared up a few things in my head. Thank you!
2

Another solution that is especially useful for getting the types of multiple properties is using Pick:

type A = Pick<T, 'prop1' | 'prop2'>

The above is equivalent to:

type A = {
  prop1: T['prop1'], 
  prop2: T['prop2']
}

Comments

0

There are couple of ways to get the type of a property of an object. Some of them are already mentioned and I would like to extend this a bit more.

As a side note, if you use Conditional Types in this task, you can avoid type errors. But that depends on the requrement.

1. Conditinal Types with infer

For example, consider the following object

const student = {
    name: 'John Doe',
    age: 22,
    dormitory: true
}

The task is to get the type of age (for example)

type AgeTypeUsingInfer<T> = T extends {age: infer K} ? K : never;
type AgeType = AgeTypeUsingInfer<typeof student>

AgeType will correctly resolve to number type.

If you pass any type which does not contain the age property, then the returned type will be never.

const musician = {
    art: 'Pop',
    city: 'Gotham'
}

type MusicianAgeType = AgeTypeUsingInfer<typeof musician>

In this case, the type of MusicianAgeType will be resolved to never without type errors.

2. Conditional Types with Indexed Access

type AgeTypeUsingConditionalTypesAndIndexedAccess<T> = T extends {age: any} ? T['age'] : never;

type AgeType2 = AgeTypeUsingConditionalTypesAndIndexedAccess<typeof student>; // number
type MusicianAgeType2 = AgeTypeUsingConditionalTypesAndIndexedAccess<typeof musician>; // never

3. With Indexed Access

type AgeTypeUsingIndexedAccess<T extends {age: any}> = T['age'];

type AgeType3 = AgeTypeUsingIndexedAccess<typeof student>; // number
type MusicianAgeType3 = AgeTypeUsingIndexedAccess<typeof musician>; // type error

Depending on the requirement, you can assign the type to never or let the type error to be surfaced immediately in case if you have passed an object which does not contain the expected property.

Hope this answer is helpful... happy coding :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.