I want to be able to type objects that expect a certain key known at compile time. This is useful when writing methods that connect a data source to let's say a React view e.g.
connectToApi(View, API, 'prop')
// 'prop' should be in the interface of `View`
Actual behavior:
Compiler fails with:
A computed property name in a type literal must refer to an expression
whose type is a literal type or a 'unique symbol' type.
Playground Link: https://agentcooper.github.io/typescript-play/#code/C4TwDgpgBA7glsAFgaQiAPMgfFAvFAbwCgpSoBtZAXQC4oAjAe0YBsIBDAOyIF8iiAxo04BnYFAAadeElQYA5ADNm8nPmJkoyxnWAAnAK4Re-IaPEBNaQhRp0SlWsIky9dnt2HjfIA
Related Issues: Don't know if this is related #13948
This is already possible:
type withKey<K extends string | number | symbol> = {
[k in K]: boolean
NiGhTTraX, lin-brian-l, tokland, Felix-Indoing, krzs, justingrant, cpylua, aminya, btmills, Nanges, and 9 more reacted with thumbs up emoji
benrosen, kenberkeley, Georgeek, and andrybicio reacted with rocket emoji
All reactions
@gigobyte any idea why your example doesn't work if the K part is inferred as follows:
abstract class Message<T extends string> {}
type MessageString<T> = T extends Message<infer S> ? S : never
type WithKey<M> = {
[m in MessageString<M>]: boolean
class M1 extends Message<'M1'>{}
class C1 implements WithKey<M1>{} // ISSUE: 'M1' property is not enforced here
I figured out that the issue has to do with the inference being made to String as opposed to a Literal; hardcodedly inferring the correct literal type fixes the issue:
abstract class Message<T extends string> {}
type MessageString<T> = T extends Message<infer S> ? 'M1' : never // infer 'M1' instead of string
type WithKey<M> = {
[m in MessageString<M>]: boolean
class Base {[index:string]: boolean }
class M1 extends Message<'M1'>{}
class C1 extends Base implements WithKey<M1>{} // NON-ISSUE: 'M1' property is enforced here
The question now is how does one arrange for a literal inference?
related issue: #27704
Resolution:
abstract class Message<T extends string> {}
type MessageString<T> = T extends Message<infer S> ? S : never
type WithKey<M> = {
[m in MessageString<M>]: boolean
class Base {[index:string]: boolean }
//class M1 extends Message<'M1'>{}
type M1 = Message<'M1'> // use a *simple* type; no union or intersection
class C1 extends Base implements WithKey<M1>{} // NON-ISSUE: 'M1' property is enforced here
I also found a trick to merge multiple generic computed parameters together. It doesn't seem that I can put multiple of these [k1 in Key1]: string
in one type, so I had to use &
type Prop1<Key1 extends string> = {
[k1 in Key1]: string
type Prop2<Key2 extends string> = {
[k2 in Key2]: boolean
type MyType<Key1 extends string, Key2 extends string> = Prop1<Key1> & Prop2<Key2>