Array values as type typescript

Typescript: Object Keys from Array

While creating a breakpoints utility on a styled-components theme (something like theme . breakpoints . down ( ‘sm’ ) ) I felt it would be valuable to have the size options strongly typed using a string array of possible sizes. Let’s set up our starting point:

const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] const values =  xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, >

At first that may seem unnecessary because an interface for values defining the possible key-value pairs would be the right way to do it, but that won’t work for my use case.

I plan on referencing that array of possible sizes in each breakpoints method since the only possible parameters should be one of those strings.

Is this overkill for a media query utility? Maybe 🤷‍♂️

String array to string literals

First we need to convert the sizes array to a string literals array. We can do this with const assertions as of Typescript 3.4. Essentially, this will assert the literal value of the array instead of widening the type to string [ ] .

const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] // type: string[] ❌ const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const // type: readonly ["xs", "sm", "md", "lg", "xl"] ✅

It’s important to do this because when we create the type for values we don’t want it to allow just any string as a key — the string must exist in sizes

Mapped type for the values object

For the values object, we’re going to create a mapped type using the in keyword and our sizes array. This will iterate over each item in sizes to create the keys then set the value type for those keys as number . The syntax for this one can be a bit difficult to read at first.

type Values =  [K in typeof sizes[number]]: number > // which is equivalent to: type Values =  xs: number sm: number md: number lg: number xl: number > // ✅

K is the type parameter — you can think of it as Key , but you’ll typically see it written as K . Then ( typeof sizes ) [ number ] is an indexed access type, which will get the resulting type from indexing typeof sizes with an index of type number . A more readable translation may be “create a key for each item in sizes then set the key value as type number .”

If we hadn’t used const assertion on sizes the type would be very general and our mapped Values type would be equivalent to:

type Values =  [x: string]: number > // ❌

Conclusion

Now we have a thorough type to apply to our values object that is based on the sizes string array. Here’s our tidy, finished code all together:

const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const type Values =  [K in typeof sizes[number]]: number > const values: Values =  xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, >

If we were to add another value to values whose key does not exist in sizes Typescript would give the following error:

const values: Values =  // . xxl: 2500, > // Type '< xs: number; sm: number; md: number; lg: number; xl: number; xxl: number; >' is not assignable to type 'Options'. // Object literal may only specify known properties, and 'xxl' does not exist in type 'Options'.

Let’s talk

I’m currently looking for a remote full-time React Engineer position. If you know of any feel free to reach out!

Источник

TypeScript: How to get types from arrays

It can be useful to get a type from an array of values so that you can use it as, say, the input to a function to restrict the values that can be passed to it.

Let’s take this simple, although contrived, example:

const animals = ['cat', 'dog', 'mouse'] function getAnimal(name: string)  return animals.find(a => a === name) > // getAnimal('cat') // OK // getAnimal('dog') // OK // getAnimal('mouse') // OK // getAnimal('elephant') // ?

Should I be able to ask for ‘elephant’?

It would be great to restrict the input to just the values in the array, especially as we know what they are:

const animals = ['cat', 'dog', 'mouse'] type Animal = 'cat' | 'dog' | 'mouse' function getAnimal(name: Animal)  return animals.find(a => a === name) > // getAnimal('cat') // OK // getAnimal('dog') // OK // getAnimal('mouse') // OK // getAnimal('elephant') // Type error

Now we get a handy type error so that we know we are passing in the wrong value:

Argument of type ‘“elephant”’ is not assignable to parameter of type ‘Animal’.

Great, but we don’t want to be duplicating all of the values in our array as a type.

Let’s generate a type from our array instead 🚀

Inferred array types

const animals = ['cat', 'dog', 'mouse'] // const animals: string[]

In the above, animals has the inferred type string[] as we have initialised the array with strings.

If we initialised the array with another type(s), say numbers const animals = [5, 10, 20] , then TypeScript would infer the type number[] , but lets stick to strings for this example.

Const assertions

In order to force the type not to be string[] , and instead to be the array of values itself, we can do the following:

const animals = ['cat', 'dog', 'mouse'] as ['cat', 'dog', 'mouse'] // const animals: ['cat', 'dog', 'mouse']

We can now use const assertions (TypeScript 3.4+) to remove the duplication. So, the following is effectively the same as the above:

const animals = ['cat', 'dog', 'mouse'] as const // const animals: readonly ['cat', 'dog', 'mouse']

Now, animals has the type readonly [‘cat’, ‘dog’, ‘mouse’] .

Getting a type from our array

Now we can get a type from our animals array using typeof :

const animals = ['cat', 'dog', 'mouse'] as const type Animal = typeof animals[number] // type Animal = 'cat' | 'dog' | 'mouse'

An array has a numeric index signature, as it’s indexed by numbers (0, 1, 2, …), so we use typeof animals[number] to get a union type of all items in our array: ‘cat’ | ‘dog’ | ‘mouse’ .

If we were to use a specific index, typeof animals[1] , we would get a type of just the value at that index: ‘dog’ .

Arrays of objects

Just in case you’re interested, if we had an array of objects instead of strings:

const animals = [  species: 'cat', name: 'Fluffy' >,  species: 'dog', name: 'Fido' >,  species: 'mouse', name: 'Trevor' > ] as const

…we would then provide the index signature for the array and the relevant object key:

type Animal = typeof animals[number]['species'] // type Animal = "cat" | "dog" | "mouse"

Hi, I’m a Senior Front-End Developer based in London. Feel free to contact me with questions, opportunities, help with open source projects, or anything else 🙂

Источник

Infer types from objects and arrays with typescript

Sometimes when we are using typescript we will need to use the object’s keys or the object’s values as a type, even some cases we need to use the values in an array as a type as well.
What I want with this post is to explain how to do it in a easy way, and by publishing it I will always have a place to return to if I forget (no-kidding) 😂

Objects

Using typeof

When we use objects, we might want to to make an interface or type to describe the types inside the object, this is fine, but for some cases where we also need to create an object based on this interface we might have some code that might look like duplicate, for example:

interface Fruit  name: string; price: number; available: boolean; > const fruit: Fruit =  name: "Apple", price: 4, available: true > 
const fruit =  name: "Apple", price: 4, available: true > type Fruit = typeof fruit; 
const initialState =  name: "Apple", price: 4, available: true > type Fruit = typeof initialState; function FruitComponent()  const [fruit, setFruit] = useState(initialState); > 

Using keyof

Using the keyof typescript will generate a new type from the keys of the object. Let’s use the following object as an example. Here is the documentation

const fruits =  "cod-1": 'Apple', "cod-2": 'Pear', "cod-3": 'Orange' > 

Suppose we have a function where we receive the code of the fruit but it must return the name of the fruit.

function getFruitName(code: string)  const name = fruits[code]; return name; > 

In this case using typescript we would have this error: . type ‘string’ can’t be used to index type < . >since string is not guaranteed to actually match the keys of the object in this case with «code-1» | «code-2» | «code-3» for this reason the code parameter should have a more exact type, we will do it using keyof

type Code = keyof typeof fruits; function getFruitName(code: Code)  const name = fruits[code]; return name; > 

We were able to do this: type Code = «code-1» | «code-2» | «code-3» , but to be honest it would only work for small cases like this, it’s not scalable.

Indexed Access Type

This is one of my favorite features, in some cases we need the type of a single property on an object/interface/type so this features will help us. Documentation here Suppose we have the following object:

const fruit =  name: "Apple", price: 4, available: true > 
// Price expects to be number type Price = typeof fruit["price"] 

By using the property in square brackets that we want the typescript to get it from, this also works for interface and type, we just have to omit the typeof here is the example for interface, it’s same for objects as a type

interface TFruit  name: string, price: number, available: boolean >; // Available expects to be boolean type Available = TFruit["available"]; 

What about valueof?

An unusual but possible use case is that we need to use the values of an object as a type, buuut valueof doesn’t exist, don’t worry, we have a solution for this and it’s quite simple. The first step is to get the type of the object keys as we did before:

const fruits =  "cod-1": 'Apple', "cod-2": 'Pear', "cod-3": 'Orange' >; type Code = keyof typeof fruits; 

Now we are going to use this type and combined with the Indexed Access Type we will get the values of the object as a type

type FruitName = typeof fruits[Code]; 

Arrays

Another use case, we could have an array whose values we will need as a type, a solution would be to duplicate the list:

const fruits = ['apple', 'pear', 'orange']; type Fruits = 'apple' | 'pear' | 'orange'; 

Buuut, no ,no, no, duplicate the list is not really correct and maintaining it could be tedious, besides I’m sure we want an elegant solutions, right? how about it looks like this:

const fruits = ['apple', 'pera', 'sandia'] as const; type Fruits = typeof fruits[number]; 
  • as const this assigns the values of the array as literal, so, the type of fruits[0] is not string it is apple literally, if you want to go deep of this, pls review this link
  • fruits[number] here we are using indexed-types again, when using it in arrays with [number] allows us to get all the values of the array as a union type, it only works with a array literal.

And thats it, hope this help you 😎

Источник

Читайте также:  Scaling an image with javascript
Оцените статью