Typescript Advanced Type NonFunctionPropertyNames Explained
I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }'
, we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]
. This looks like a definition of an array {...}[keyof T]
but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T]
.
typescript typescript-typings
add a comment |
I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }'
, we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]
. This looks like a definition of an array {...}[keyof T]
but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T]
.
typescript typescript-typings
1
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
typeof
is used whenever you want to get the type of a value. You can usetypeof variable
to get the type of the variable, ortypeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))
– Titian Cernicova-Dragomir
yesterday
add a comment |
I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }'
, we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]
. This looks like a definition of an array {...}[keyof T]
but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T]
.
typescript typescript-typings
I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }'
, we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]
. This looks like a definition of an array {...}[keyof T]
but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T]
.
typescript typescript-typings
typescript typescript-typings
asked yesterday
HivagaHivaga
319213
319213
1
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
typeof
is used whenever you want to get the type of a value. You can usetypeof variable
to get the type of the variable, ortypeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))
– Titian Cernicova-Dragomir
yesterday
add a comment |
1
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
typeof
is used whenever you want to get the type of a value. You can usetypeof variable
to get the type of the variable, ortypeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))
– Titian Cernicova-Dragomir
yesterday
1
1
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
typeof
is used whenever you want to get the type of a value. You can use typeof variable
to get the type of the variable, or typeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))– Titian Cernicova-Dragomir
yesterday
typeof
is used whenever you want to get the type of a value. You can use typeof variable
to get the type of the variable, or typeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))– Titian Cernicova-Dragomir
yesterday
add a comment |
1 Answer
1
active
oldest
votes
What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:
type Foo = { foo: number }['foo'] // is number
If you are indexing using a union of several properties you get a union of all the property types:
type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar'] // string | number
If you index using all keys you get the a union of all property types:
type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz'] // string | number | boolean
But to get a union of all property names you can use keyof
so the above type can also be written as:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean
The type { [K in keyof T]: K }
evaluates to an object type, where the keys are typed as the same literal type representing the key:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }
What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never
instead:
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }
So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never
. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O
as before (since the type has the same keys as O
):
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;
never
is always removed from unions, so we get at the end a union of just those object keys that were not never.
Make O
a type parameter and you have a reusable type to get the non function keys:
type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54251847%2ftypescript-advanced-type-nonfunctionpropertynames-explained%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:
type Foo = { foo: number }['foo'] // is number
If you are indexing using a union of several properties you get a union of all the property types:
type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar'] // string | number
If you index using all keys you get the a union of all property types:
type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz'] // string | number | boolean
But to get a union of all property names you can use keyof
so the above type can also be written as:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean
The type { [K in keyof T]: K }
evaluates to an object type, where the keys are typed as the same literal type representing the key:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }
What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never
instead:
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }
So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never
. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O
as before (since the type has the same keys as O
):
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;
never
is always removed from unions, so we get at the end a union of just those object keys that were not never.
Make O
a type parameter and you have a reusable type to get the non function keys:
type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"
add a comment |
What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:
type Foo = { foo: number }['foo'] // is number
If you are indexing using a union of several properties you get a union of all the property types:
type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar'] // string | number
If you index using all keys you get the a union of all property types:
type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz'] // string | number | boolean
But to get a union of all property names you can use keyof
so the above type can also be written as:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean
The type { [K in keyof T]: K }
evaluates to an object type, where the keys are typed as the same literal type representing the key:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }
What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never
instead:
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }
So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never
. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O
as before (since the type has the same keys as O
):
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;
never
is always removed from unions, so we get at the end a union of just those object keys that were not never.
Make O
a type parameter and you have a reusable type to get the non function keys:
type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"
add a comment |
What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:
type Foo = { foo: number }['foo'] // is number
If you are indexing using a union of several properties you get a union of all the property types:
type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar'] // string | number
If you index using all keys you get the a union of all property types:
type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz'] // string | number | boolean
But to get a union of all property names you can use keyof
so the above type can also be written as:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean
The type { [K in keyof T]: K }
evaluates to an object type, where the keys are typed as the same literal type representing the key:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }
What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never
instead:
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }
So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never
. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O
as before (since the type has the same keys as O
):
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;
never
is always removed from unions, so we get at the end a union of just those object keys that were not never.
Make O
a type parameter and you have a reusable type to get the non function keys:
type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"
What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:
type Foo = { foo: number }['foo'] // is number
If you are indexing using a union of several properties you get a union of all the property types:
type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar'] // string | number
If you index using all keys you get the a union of all property types:
type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz'] // string | number | boolean
But to get a union of all property names you can use keyof
so the above type can also be written as:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean
The type { [K in keyof T]: K }
evaluates to an object type, where the keys are typed as the same literal type representing the key:
type O = { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }
What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never
instead:
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }
So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never
. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O
as before (since the type has the same keys as O
):
type O = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;
never
is always removed from unions, so we get at the end a union of just those object keys that were not never.
Make O
a type parameter and you have a reusable type to get the non function keys:
type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"
answered yesterday
Titian Cernicova-DragomirTitian Cernicova-Dragomir
59.9k33553
59.9k33553
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54251847%2ftypescript-advanced-type-nonfunctionpropertynames-explained%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)
– Titian Cernicova-Dragomir
yesterday
@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?
– Hivaga
yesterday
typeof
is used whenever you want to get the type of a value. You can usetypeof variable
to get the type of the variable, ortypeof Class
to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))– Titian Cernicova-Dragomir
yesterday