0

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

2
  • const sizes: { [K in keyof typeof sizes]: CSSObject } = {... yup kind of this but it's not working, it says Type parameter 'K' has a circular constraint. Commented Feb 13, 2020 at 13:59
  • Oh, I misunderstood the problem, but I think I get it now. Commented Feb 13, 2020 at 14:04

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

Comments

0

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>()({.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.