typescript mapped tuple lookup types?
up vote
2
down vote
favorite
Now that typescript 3.1 introduced mapped tuple types, I was hoping this code sample would work:
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKey[K]> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
return x['General_Language'] === 'de' // would want compilation error 'de' not in 'en' | 'sl'
})
But it doesn't. The errors are:
ttt.ts:7:83 - error TS2536: Type 'K' cannot be used to index type 'SettingKey'.
7 export function fetchSetting<K extends (keyof SettingKey)>(...keys: K): Promise<SettingKey[K]> {
~~~~~~~~~~~~~
ttt.ts:11:12 - error TS2571: Object is of type 'unknown'.
11 return x['General_Language'] === 'de'
~
Clearly the second error is a consequence of the first one, so that's not really a concern. The first one is the problematic one.
keys is an array of keyof SettingKey
, and so I would hope that SettingKey[K]
would be an array of the types of the listed properties (so, concretely in the code sample I put, it would be ['en' | 'sl', number]
. From the pull request introducing the typescript feature:
If T is an array type S we map to an array type R, where R is an instantiation of X with S substituted for T[P].
But that holds I guess for mapped types only, and here I have a lookup type, that would be the reason why it doesn't work I guess?
I think what I want to express is clear; can this be made type-safe in typescript?
typescript
add a comment |
up vote
2
down vote
favorite
Now that typescript 3.1 introduced mapped tuple types, I was hoping this code sample would work:
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKey[K]> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
return x['General_Language'] === 'de' // would want compilation error 'de' not in 'en' | 'sl'
})
But it doesn't. The errors are:
ttt.ts:7:83 - error TS2536: Type 'K' cannot be used to index type 'SettingKey'.
7 export function fetchSetting<K extends (keyof SettingKey)>(...keys: K): Promise<SettingKey[K]> {
~~~~~~~~~~~~~
ttt.ts:11:12 - error TS2571: Object is of type 'unknown'.
11 return x['General_Language'] === 'de'
~
Clearly the second error is a consequence of the first one, so that's not really a concern. The first one is the problematic one.
keys is an array of keyof SettingKey
, and so I would hope that SettingKey[K]
would be an array of the types of the listed properties (so, concretely in the code sample I put, it would be ['en' | 'sl', number]
. From the pull request introducing the typescript feature:
If T is an array type S we map to an array type R, where R is an instantiation of X with S substituted for T[P].
But that holds I guess for mapped types only, and here I have a lookup type, that would be the reason why it doesn't work I guess?
I think what I want to express is clear; can this be made type-safe in typescript?
typescript
2
If you want['en' | 'sl', number]
then you should be looking upx[0]
, notx['General_Language']
. That's a separate error from the one about mapping tuples.
– jcalz
Nov 10 at 19:39
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Now that typescript 3.1 introduced mapped tuple types, I was hoping this code sample would work:
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKey[K]> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
return x['General_Language'] === 'de' // would want compilation error 'de' not in 'en' | 'sl'
})
But it doesn't. The errors are:
ttt.ts:7:83 - error TS2536: Type 'K' cannot be used to index type 'SettingKey'.
7 export function fetchSetting<K extends (keyof SettingKey)>(...keys: K): Promise<SettingKey[K]> {
~~~~~~~~~~~~~
ttt.ts:11:12 - error TS2571: Object is of type 'unknown'.
11 return x['General_Language'] === 'de'
~
Clearly the second error is a consequence of the first one, so that's not really a concern. The first one is the problematic one.
keys is an array of keyof SettingKey
, and so I would hope that SettingKey[K]
would be an array of the types of the listed properties (so, concretely in the code sample I put, it would be ['en' | 'sl', number]
. From the pull request introducing the typescript feature:
If T is an array type S we map to an array type R, where R is an instantiation of X with S substituted for T[P].
But that holds I guess for mapped types only, and here I have a lookup type, that would be the reason why it doesn't work I guess?
I think what I want to express is clear; can this be made type-safe in typescript?
typescript
Now that typescript 3.1 introduced mapped tuple types, I was hoping this code sample would work:
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKey[K]> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
return x['General_Language'] === 'de' // would want compilation error 'de' not in 'en' | 'sl'
})
But it doesn't. The errors are:
ttt.ts:7:83 - error TS2536: Type 'K' cannot be used to index type 'SettingKey'.
7 export function fetchSetting<K extends (keyof SettingKey)>(...keys: K): Promise<SettingKey[K]> {
~~~~~~~~~~~~~
ttt.ts:11:12 - error TS2571: Object is of type 'unknown'.
11 return x['General_Language'] === 'de'
~
Clearly the second error is a consequence of the first one, so that's not really a concern. The first one is the problematic one.
keys is an array of keyof SettingKey
, and so I would hope that SettingKey[K]
would be an array of the types of the listed properties (so, concretely in the code sample I put, it would be ['en' | 'sl', number]
. From the pull request introducing the typescript feature:
If T is an array type S we map to an array type R, where R is an instantiation of X with S substituted for T[P].
But that holds I guess for mapped types only, and here I have a lookup type, that would be the reason why it doesn't work I guess?
I think what I want to express is clear; can this be made type-safe in typescript?
typescript
typescript
asked Nov 10 at 19:19
Emmanuel Touzery
5,81314251
5,81314251
2
If you want['en' | 'sl', number]
then you should be looking upx[0]
, notx['General_Language']
. That's a separate error from the one about mapping tuples.
– jcalz
Nov 10 at 19:39
add a comment |
2
If you want['en' | 'sl', number]
then you should be looking upx[0]
, notx['General_Language']
. That's a separate error from the one about mapping tuples.
– jcalz
Nov 10 at 19:39
2
2
If you want
['en' | 'sl', number]
then you should be looking up x[0]
, not x['General_Language']
. That's a separate error from the one about mapping tuples.– jcalz
Nov 10 at 19:39
If you want
['en' | 'sl', number]
then you should be looking up x[0]
, not x['General_Language']
. That's a separate error from the one about mapping tuples.– jcalz
Nov 10 at 19:39
add a comment |
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
To have a mapped tuple you need a mapped type, that will map the original tuple (in the type parameter K
) to the new tuple type
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
type SettingKeyProp<P extends keyof SettingKey> = SettingKey[P]
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in keyof K]: K[P] extends keyof SettingKey ? SettingKey[K[P]]: never
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// x[0] is 'en' | 'sl'
return x[0] === 'de' /// since you want a tuple, you should index by number not name
})
If you want to index by name that is also possible, but the mapped type should map over the values in the array not the keys in the array:
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in K[number]]: SettingKey[P]
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// you can access by name
return x.General_Language === 'de'
})
1
great and thank you! Note that the type parameter forSettingKeyArray
looks a little frightening, I found out that sayingtype SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.
– Emmanuel Touzery
Nov 11 at 8:30
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
To have a mapped tuple you need a mapped type, that will map the original tuple (in the type parameter K
) to the new tuple type
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
type SettingKeyProp<P extends keyof SettingKey> = SettingKey[P]
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in keyof K]: K[P] extends keyof SettingKey ? SettingKey[K[P]]: never
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// x[0] is 'en' | 'sl'
return x[0] === 'de' /// since you want a tuple, you should index by number not name
})
If you want to index by name that is also possible, but the mapped type should map over the values in the array not the keys in the array:
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in K[number]]: SettingKey[P]
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// you can access by name
return x.General_Language === 'de'
})
1
great and thank you! Note that the type parameter forSettingKeyArray
looks a little frightening, I found out that sayingtype SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.
– Emmanuel Touzery
Nov 11 at 8:30
add a comment |
up vote
3
down vote
accepted
To have a mapped tuple you need a mapped type, that will map the original tuple (in the type parameter K
) to the new tuple type
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
type SettingKeyProp<P extends keyof SettingKey> = SettingKey[P]
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in keyof K]: K[P] extends keyof SettingKey ? SettingKey[K[P]]: never
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// x[0] is 'en' | 'sl'
return x[0] === 'de' /// since you want a tuple, you should index by number not name
})
If you want to index by name that is also possible, but the mapped type should map over the values in the array not the keys in the array:
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in K[number]]: SettingKey[P]
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// you can access by name
return x.General_Language === 'de'
})
1
great and thank you! Note that the type parameter forSettingKeyArray
looks a little frightening, I found out that sayingtype SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.
– Emmanuel Touzery
Nov 11 at 8:30
add a comment |
up vote
3
down vote
accepted
up vote
3
down vote
accepted
To have a mapped tuple you need a mapped type, that will map the original tuple (in the type parameter K
) to the new tuple type
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
type SettingKeyProp<P extends keyof SettingKey> = SettingKey[P]
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in keyof K]: K[P] extends keyof SettingKey ? SettingKey[K[P]]: never
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// x[0] is 'en' | 'sl'
return x[0] === 'de' /// since you want a tuple, you should index by number not name
})
If you want to index by name that is also possible, but the mapped type should map over the values in the array not the keys in the array:
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in K[number]]: SettingKey[P]
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// you can access by name
return x.General_Language === 'de'
})
To have a mapped tuple you need a mapped type, that will map the original tuple (in the type parameter K
) to the new tuple type
export interface SettingKey {
General_Language: 'en' | 'sl';
Map_InitialLongitude: number;
Map_InitialLatitude: number;
}
type SettingKeyProp<P extends keyof SettingKey> = SettingKey[P]
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in keyof K]: K[P] extends keyof SettingKey ? SettingKey[K[P]]: never
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// x[0] is 'en' | 'sl'
return x[0] === 'de' /// since you want a tuple, you should index by number not name
})
If you want to index by name that is also possible, but the mapped type should map over the values in the array not the keys in the array:
type SettingKeyArray<K extends { [n: number]: keyof SettingKey }> = {
[P in K[number]]: SettingKey[P]
}
export function fetchSetting<K extends (keyof SettingKey)>
(...keys: K): Promise<SettingKeyArray<K>> {
return null as any;
}
fetchSetting('General_Language', 'Map_InitialLongitude').then(x => {
// you can access by name
return x.General_Language === 'de'
})
edited Nov 10 at 19:49
answered Nov 10 at 19:42
Titian Cernicova-Dragomir
53.4k33250
53.4k33250
1
great and thank you! Note that the type parameter forSettingKeyArray
looks a little frightening, I found out that sayingtype SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.
– Emmanuel Touzery
Nov 11 at 8:30
add a comment |
1
great and thank you! Note that the type parameter forSettingKeyArray
looks a little frightening, I found out that sayingtype SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.
– Emmanuel Touzery
Nov 11 at 8:30
1
1
great and thank you! Note that the type parameter for
SettingKeyArray
looks a little frightening, I found out that saying type SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.– Emmanuel Touzery
Nov 11 at 8:30
great and thank you! Note that the type parameter for
SettingKeyArray
looks a little frightening, I found out that saying type SettingKeyArray<K extends (keyof SettingKey)> = ...
works just as well.– Emmanuel Touzery
Nov 11 at 8:30
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.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- 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%2f53242568%2ftypescript-mapped-tuple-lookup-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
2
If you want
['en' | 'sl', number]
then you should be looking upx[0]
, notx['General_Language']
. That's a separate error from the one about mapping tuples.– jcalz
Nov 10 at 19:39