1

I'm working in styled-system and am trying to type a pattern that is repeatedly used in that library.

const space: { [key: string]: string } = [
  '0.25rem',
  '0.5rem',
  '1rem',
  '2rem',
  '4rem',
  '6rem',
];

space.xs = space[0];
space.sm = space[1];
space.md = space[2];
space.lg = space[3];
space.xl = space[4];
space.xxl = space[5];

I'm getting this error:

Type 'string[]' is not assignable to type '{ [key: string]: string; }'.
  Index signature is missing in type 'string[]'.ts(2322)

I'm not certain on how to reconcile a number key and a string key for an Array index or how to merge the object and array. It works as expected in vanilla javascript. The easy work around is setting string to any but I would like to know why this doesn't work as expected. I would think that an associative array pattern would be acceptable.

2
  • isn't space sort of a dictionary? it's missing key, that's what I think its complaining for. All values may be kept in a string[] then assignment through that array to xs/sm/md should do it. Commented Mar 10, 2020 at 22:20
  • So, you have the map those in order to actually add them? Commented Mar 10, 2020 at 22:34

3 Answers 3

2

Short Answer

To add types to your existing JavaScript code, the following approach works.

Here it is in the playground.

type Props = Record<"xs" | "sm" | "md" | "lg" | "xl" | "xxl", string>;
type ArrayWithProps = string[] & Partial<Props>;

const space: ArrayWithProps = [
  "0.25rem",
  "0.5rem",
  "1rem",
  "2rem",
  "4rem",
  "6rem"
];

space.xs = space[0];
space.sm = space[1];
space.md = space[2];
space.lg = space[3];
space.xl = space[4];
space.xxl = space[5];

Details

First we define Props using the Record type. The Record type is a built-in type that defines an index signature. In our case, the equivalent index signature looks like this:

type Props = {
  [K in "xs" | "sm" | "md" | "lg" | "xl" | "xxl"]: string;
};

This index signature works (where [K in string] does not work), because we are using string literal types instead of the wider string type.

Next, we create a type named ArrayWithProps. This union combines the properties of the string[] type with the properties of the Props type. We use Partial to make each of the Props optional.

The result passes type checking, and it leaves the existing JavaScript code unchanged.

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

3 Comments

That's a pretty neat attempt. Also new to me :)
Thanks for the great explanation!
You're welcome @jango. Welcome to StackOverflow. :-)
0

Object notation is incorrect.

{ [key: string]: string } means an object, where keys and values are strings. But in fact you have an array of strings.

So the correct notation will be:

const space: string[] = [
  '0.25rem',
  '0.5rem',
  '1rem',
  '2rem',
  '4rem',
  '6rem',
];

P.S. if you want to keep it as object then you need something like this

const space: { [key: string]: string } = {
  xs: '0.25rem',
  sm: '0.5rem',
  md: '1rem',
  lg: '2rem',
  xl: '4rem',
  xxl: '6rem',
};

5 Comments

How do you assign the short hand objects then (i.e. xs)?
@jango check out updated answer, but it is not 100% clear what you are trying to accomplish
Yeah, but then you'd not be able to do space[0]. Shaun had a great implementation which was exactly what I needed.
@jango you should know that assigning props to an array is actually poor solution and leads to an unexpected behavior. it is allowed by the language, but it does not mean you should do that
For example those props are not included while looping through for
0

TypeScript uses static typings and type checking at compile time. Therefore assigning a string[] to { [key: string]: string } does not work and hence the error. If you want to use an object, you could do something like:

const space: { [key: string]: string } = {};

for (const s of ['0.25rem', '0.5rem', '1rem', '2rem', '4rem', '6rem']) {
  space[s] = s;
}

console.log(space);

// { 0.25rem: "0.25rem", 0.5rem: "0.5rem", 1rem: "1rem", 2rem: "2rem", 4rem: "4rem", 6rem: "6rem" }

Edit:

You could use a Map:

const map: Map<string, string> = new Map([
  ['xs', '0.25rem'],
  ['sm', '0.5rem'],
  ['md', '1rem'],
  ['lg', '2rem'],
  ['xl', '4rem'],
  ['xxl', '6rem']
]);

console.log(map.get('xs'));
// 0.25rem

console.log(Array.from(map.values()));
// ["0.25rem", "0.5rem", "1rem", "2rem", "4rem", "6rem"]

1 Comment

Sadly, that doesn't work for us because we need the Array functions to be available for styled-systems. It uses both formats as a way to responsively adjust the css in our codebase.

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.