Generic function and index types
I'm looking to implement a generic function which accepts a single type argument "T" which is an object. It uses keyof T
to specify the first argument to the function "property". Now the part I can't get working, I want to use the type of T[typeof property]
to specify the type of the second argument to the function "value".
This gets me close to the ideal usage,
type Person =
name: string;
age: number;
;
function filter<T>(property: keyof T, value: T[typeof property])
return `$property $value`
filter<Person>("name", 123);
With the above we get type checking for the "property" argument, correctly restricting it to only "name" or "age". Unfortunately for the value argument it accepts either a number or string, so it seems to be creating a union of all the keyof types of Person.
Any ideas?
typescript
add a comment |
I'm looking to implement a generic function which accepts a single type argument "T" which is an object. It uses keyof T
to specify the first argument to the function "property". Now the part I can't get working, I want to use the type of T[typeof property]
to specify the type of the second argument to the function "value".
This gets me close to the ideal usage,
type Person =
name: string;
age: number;
;
function filter<T>(property: keyof T, value: T[typeof property])
return `$property $value`
filter<Person>("name", 123);
With the above we get type checking for the "property" argument, correctly restricting it to only "name" or "age". Unfortunately for the value argument it accepts either a number or string, so it seems to be creating a union of all the keyof types of Person.
Any ideas?
typescript
add a comment |
I'm looking to implement a generic function which accepts a single type argument "T" which is an object. It uses keyof T
to specify the first argument to the function "property". Now the part I can't get working, I want to use the type of T[typeof property]
to specify the type of the second argument to the function "value".
This gets me close to the ideal usage,
type Person =
name: string;
age: number;
;
function filter<T>(property: keyof T, value: T[typeof property])
return `$property $value`
filter<Person>("name", 123);
With the above we get type checking for the "property" argument, correctly restricting it to only "name" or "age". Unfortunately for the value argument it accepts either a number or string, so it seems to be creating a union of all the keyof types of Person.
Any ideas?
typescript
I'm looking to implement a generic function which accepts a single type argument "T" which is an object. It uses keyof T
to specify the first argument to the function "property". Now the part I can't get working, I want to use the type of T[typeof property]
to specify the type of the second argument to the function "value".
This gets me close to the ideal usage,
type Person =
name: string;
age: number;
;
function filter<T>(property: keyof T, value: T[typeof property])
return `$property $value`
filter<Person>("name", 123);
With the above we get type checking for the "property" argument, correctly restricting it to only "name" or "age". Unfortunately for the value argument it accepts either a number or string, so it seems to be creating a union of all the keyof types of Person.
Any ideas?
typescript
typescript
asked Nov 14 '18 at 11:58
rssfrncsrssfrncs
558215
558215
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
You need to create a relation between the two parameters, after all you could potentially be passing in multiple keys, and multiple values, why would the compiler asume that value
should have the type of the key specified in the property
parameter.
To create the relation you will need an extra type parameter to represent the property as a literal type
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok
The problem with the above implementation is that you have to specify the additional type parameter since typescript does not support partial type inference. (Hopefuly it will soon be possible as this issue proposes, it's slated for January 2018 but is has been pushed back several times)
To fix this inconvenience we can create a function that returns a function and fix the T
parameter in the first call and let K
be inferred in the second call
function filter<T>()
return function <K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok
Or depending on what you plan to do with filter
you can leave the function with two type parameters and use the return type as the source for the inference of T
:
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value` as IFiler<T>
type IFiler<T> = string & _isFilterFor: T
class Query<T>
addFilter(f: IFiler<T>)
var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error
q.addFilter(filter('age', 12)) //ok
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
add a comment |
This way you don't have to change your code
type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) => string;
function filter(property: string, value: any)
return `$property $value`;
type Person =
name: string;
age: number;
;
const result = (filter as Fn<Person>)("name", 123);
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
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%2f53299743%2fgeneric-function-and-index-types%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
You need to create a relation between the two parameters, after all you could potentially be passing in multiple keys, and multiple values, why would the compiler asume that value
should have the type of the key specified in the property
parameter.
To create the relation you will need an extra type parameter to represent the property as a literal type
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok
The problem with the above implementation is that you have to specify the additional type parameter since typescript does not support partial type inference. (Hopefuly it will soon be possible as this issue proposes, it's slated for January 2018 but is has been pushed back several times)
To fix this inconvenience we can create a function that returns a function and fix the T
parameter in the first call and let K
be inferred in the second call
function filter<T>()
return function <K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok
Or depending on what you plan to do with filter
you can leave the function with two type parameters and use the return type as the source for the inference of T
:
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value` as IFiler<T>
type IFiler<T> = string & _isFilterFor: T
class Query<T>
addFilter(f: IFiler<T>)
var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error
q.addFilter(filter('age', 12)) //ok
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
add a comment |
You need to create a relation between the two parameters, after all you could potentially be passing in multiple keys, and multiple values, why would the compiler asume that value
should have the type of the key specified in the property
parameter.
To create the relation you will need an extra type parameter to represent the property as a literal type
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok
The problem with the above implementation is that you have to specify the additional type parameter since typescript does not support partial type inference. (Hopefuly it will soon be possible as this issue proposes, it's slated for January 2018 but is has been pushed back several times)
To fix this inconvenience we can create a function that returns a function and fix the T
parameter in the first call and let K
be inferred in the second call
function filter<T>()
return function <K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok
Or depending on what you plan to do with filter
you can leave the function with two type parameters and use the return type as the source for the inference of T
:
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value` as IFiler<T>
type IFiler<T> = string & _isFilterFor: T
class Query<T>
addFilter(f: IFiler<T>)
var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error
q.addFilter(filter('age', 12)) //ok
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
add a comment |
You need to create a relation between the two parameters, after all you could potentially be passing in multiple keys, and multiple values, why would the compiler asume that value
should have the type of the key specified in the property
parameter.
To create the relation you will need an extra type parameter to represent the property as a literal type
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok
The problem with the above implementation is that you have to specify the additional type parameter since typescript does not support partial type inference. (Hopefuly it will soon be possible as this issue proposes, it's slated for January 2018 but is has been pushed back several times)
To fix this inconvenience we can create a function that returns a function and fix the T
parameter in the first call and let K
be inferred in the second call
function filter<T>()
return function <K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok
Or depending on what you plan to do with filter
you can leave the function with two type parameters and use the return type as the source for the inference of T
:
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value` as IFiler<T>
type IFiler<T> = string & _isFilterFor: T
class Query<T>
addFilter(f: IFiler<T>)
var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error
q.addFilter(filter('age', 12)) //ok
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");
You need to create a relation between the two parameters, after all you could potentially be passing in multiple keys, and multiple values, why would the compiler asume that value
should have the type of the key specified in the property
parameter.
To create the relation you will need an extra type parameter to represent the property as a literal type
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person, "name">("name", 123); // error
filter<Person, "age">("age", 123); // ok
The problem with the above implementation is that you have to specify the additional type parameter since typescript does not support partial type inference. (Hopefuly it will soon be possible as this issue proposes, it's slated for January 2018 but is has been pushed back several times)
To fix this inconvenience we can create a function that returns a function and fix the T
parameter in the first call and let K
be inferred in the second call
function filter<T>()
return function <K extends keyof T>(property: K, value: T[K])
return `$property $value`
filter<Person>()("name", 123); // error
filter<Person>()("age", 123); // ok
Or depending on what you plan to do with filter
you can leave the function with two type parameters and use the return type as the source for the inference of T
:
function filter<T, K extends keyof T>(property: K, value: T[K])
return `$property $value` as IFiler<T>
type IFiler<T> = string & _isFilterFor: T
class Query<T>
addFilter(f: IFiler<T>)
var q = new Query<Person>();
// T will be inferred to Person in filter because we assign in to a parameter expecting IFilter<Person>
q.addFilter(filter('age', "")) //error
q.addFilter(filter('age', 12)) //ok
// Same as above but assigning to a variable:
var f: IFiler<Person> = filter('age', 12);
var f2: IFiler<Person> = filter('age', "12");
edited Nov 14 '18 at 12:10
answered Nov 14 '18 at 12:05
Titian Cernicova-DragomirTitian Cernicova-Dragomir
70k34866
70k34866
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
add a comment |
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
This is great! Thanks for the pointer about partial type inference and the issue to keep track of.
– rssfrncs
Nov 15 '18 at 11:11
add a comment |
This way you don't have to change your code
type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) => string;
function filter(property: string, value: any)
return `$property $value`;
type Person =
name: string;
age: number;
;
const result = (filter as Fn<Person>)("name", 123);
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
add a comment |
This way you don't have to change your code
type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) => string;
function filter(property: string, value: any)
return `$property $value`;
type Person =
name: string;
age: number;
;
const result = (filter as Fn<Person>)("name", 123);
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
add a comment |
This way you don't have to change your code
type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) => string;
function filter(property: string, value: any)
return `$property $value`;
type Person =
name: string;
age: number;
;
const result = (filter as Fn<Person>)("name", 123);
This way you don't have to change your code
type Fn<T> = <K extends keyof T>(prop: K, value: T[K]) => string;
function filter(property: string, value: any)
return `$property $value`;
type Person =
name: string;
age: number;
;
const result = (filter as Fn<Person>)("name", 123);
answered Nov 14 '18 at 16:03
Daniel PérezDaniel Pérez
497312
497312
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
add a comment |
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
Appreciate the alternative solution
– rssfrncs
Nov 15 '18 at 11:10
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%2f53299743%2fgeneric-function-and-index-types%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