Deep
Deep

Reputation: 38

Typescript: Define object type without defining types for keys to get keyof

Here I want to define the type for object value only. In order to get types for keys. Is there any way to get without defining types for each value.

const sizes: Record<string, CSSObject>= {
  md: {
    padding: [10, 24],
    fontSize: 'medium',
  },
  xs: {
    padding: [6, 12],
    fontSize: 'small',
  },
  sm: {
    padding: [8, 16],
    fontSize: 'small',
  },
  lg: {
    padding: [14, 30],
    fontSize: 'large',
  },
} as const;

// Expecting 'md' | 'xs' | 'sm' | 'lg'
type Sizes = keyof typeof sizes;

// But it is string

Upvotes: 0

Views: 390

Answers (2)

chrmcg
chrmcg

Reputation: 21

Just wanted to add on to @kaya3's very helpful answer:

The helper function here works specifically with CSSObject. I needed to use this helper on multiple objects with different value types, so I wrote a version that lets you pass in any type:

function constrainValuesAndInferKeys<V>() {
    return function <K extends PropertyKey>(obj: Record<K, V>) {
        return obj;
    }
}

const sizes = constrainValuesAndInferKeys<CSSProperties>()({
  md: {
    padding: [10, 24],
    fontSize: 'medium',
  },
  // ...
});

type Sizes = keyof typeof sizes;
// 'md' | 'xs' | 'sm' | 'lg'

The downside is you have to add that extra () in constrainValuesAndInferKeys<CSSProperties>()({.

Upvotes: 0

kaya3
kaya3

Reputation: 51037

As I understand it, you want to infer the keys 'md' | 'xs' | 'sm' | 'lg' from the object's actual properties, but you also want the type-checker to make sure that the properties' values are of type CSSObject.

The problem is that you would need a type annotation on the object literal to check the values are CSSObject; but if you use a type annotation then keyof ... will get the keys from the annotated type, not the object itself.

The way around this is to use a generic helper function, so that the record's key type is inferred while the value type is specified:

function helper<K extends PropertyKey>(obj: Record<K, CSSObject>): Record<K, CSSObject> {
    return obj;
}

const sizes = helper({
  md: {
    padding: [10, 24],
    fontSize: 'medium',
  },
  // ...
});

type Sizes = keyof typeof sizes;
// 'md' | 'xs' | 'sm' | 'lg'

Playground Link

Upvotes: 2

Related Questions