Typescript: Generic computed properties

Created on 4 Jan 2019  ·  6Comments  ·  Source: microsoft/TypeScript

TypeScript Version: [email protected]

Search Terms: generic computed properties

Code

type withKey<K> = {
    [K]: boolean
}

const X: withKey<'foo'> = {
    foo: true
}

Expected behavior:

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 https://github.com/Microsoft/TypeScript/issues/13948

Question

Most helpful comment

This is already possible:

type withKey<K extends string | number | symbol> = {
    [k in K]: boolean
}

All 6 comments

Maybe?

type withKey<K extends string> = Record<K, boolean>

This is already possible:

type withKey<K extends string | number | symbol> = {
    [k in K]: boolean
}

Perfect, thank you very much!

@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: https://github.com/microsoft/TypeScript/issues/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
Was this page helpful?
0 / 5 - 0 ratings