Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Union from values in an object #17908

Open
koodiohto opened this issue Oct 24, 2022 · 6 comments · May be fixed by #17910
Open

Union from values in an object #17908

koodiohto opened this issue Oct 24, 2022 · 6 comments · May be fixed by #17910
Labels
new-challenge Propose a new challenge, a PR will be auto generated

Comments

@koodiohto
Copy link

koodiohto commented Oct 24, 2022

Info

difficulty: medium
title: Union from values in an object
tags: union, array, object, object-keys, const assertions

Question

Create a generalizable type UnionOfArrayValuesInObject that can be used to create a union type of all the values from all the array values inside an object that contains object keys and arrays as the objects.

The task is solvable without using utility types.

const object1WithObjectArrays= {
  key: ['one', 'two', 'three']
} as const

const object2WithObjectArrays= {
  key: [1, 2, 3]
} as const

const AllMeasurementUnits = {
  weight: ['g', 'kg', 'pound'],
  length: ['cm', 'm', 'inch']
} as const

type UnionOfArrayValuesInObject<T> = any

const oneString: UnionOfArrayValuesInObject<typeof object1WithObjectArrays> = 'one' //should be a valid type
const fourString: UnionOfArrayValuesInObject<typeof object2WithObjectArrays> = 'four' //shouldn't be a valid type

const oneNumber: UnionOfArrayValuesInObject<typeof object2WithObjectArrays> = 1 //should be a valid type

const grams: UnionOfArrayValuesInObject<typeof AllMeasurementUnits> = 'g' //should be a valid type
const ounce: UnionOfArrayValuesInObject<typeof AllMeasurementUnits> = 'ounce' //shouldn't be a valid type

Template

const object1WithObjectArrays= {
  key: ['one', 'two', 'three']
} as const

const object2WithObjectArrays= {
  key: [1, 2, 3]
} as const

const AllMeasurementUnits = {
  weight: ['g', 'kg', 'pound'],
  length: ['cm', 'm', 'inch']
} as const

type UnionOfArrayValuesInObject<T> = any

const oneString: UnionOfArrayValuesInObject<typeof object1WithObjectArrays> = 'one' //should be a valid type
const oneNumber: UnionOfArrayValuesInObject<typeof object2WithObjectArrays> = 1 //should be a valid type
const grams: UnionOfArrayValuesInObject<typeof AllMeasurementUnits> = 'g' //should be a valid type

Test Cases

import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<UnionOfArrayValuesInObject<typeof object1WithObjectArrays>, 'one' | 'two' | 'three'>>,
  Expect<Equal<UnionOfArrayValuesInObject<typeof object2WithObjectArrays>, 1 | 2 | 3 >>,
  Expect<Equal<UnionOfArrayValuesInObject<typeof AllMeasurementUnits>, 'g' | 'kg' | 'pound' | 'cm' | 'm' | 'inch'>>,
]
@koodiohto koodiohto added the new-challenge Propose a new challenge, a PR will be auto generated label Oct 24, 2022
@koodiohto koodiohto changed the title Union type from all the array values inside an object Union from values in an object Oct 24, 2022
@github-actions github-actions bot linked a pull request Oct 24, 2022 that will close this issue
@github-actions
Copy link
Contributor

github-actions bot commented Oct 24, 2022

#17910 - Pull Request updated.

2022-11-14T07:13:57.915Z Preview in Playground

@jiangshanmeta
Copy link
Member

type AnyMeasurementUnit<T> = {
  [K in keyof T]:T[K] extends readonly any[]?T[K][number]:never
}[keyof T]

@koodiohto Your description is too specific. It's better to have a broader description

@koodiohto
Copy link
Author

Thank you for your feedback and one possible solution! I'd appreciate if you still have time to point out which part of the description in specific you think should be made broader. Or do you mean that this example shouldn't be coupled to the measurement units, but be more generic? Thanks!

Btw. this is how I ended up solving this (without using generics in the type):

type AnyMeasurementUnit = typeof AllMeasurementUnits[keyof typeof AllMeasurementUnits][number]

@jiangshanmeta
Copy link
Member

@koodiohto

Yesterday I edited your test case from Expect<Equal<AnyMeasurementUnit, 'g' | 'kg' | 'pound' | 'cm' | 'm' | 'inch'>>, to Expect<Equal<AnyMeasurementUnit<typeof AllMeasurementUnits>, 'g' | 'kg' | 'pound' | 'cm' | 'm' | 'inch'>>,. At first I thought it was just a typo. After you replied me , I finally realize that what you want to implement is just a spefic type , and what you described in the Question section is only for this spefic type.

Actually, I hope your type AnyMeasurementUnit to be a generic type , like the built-in Partial<T> type, which accept a type parameter rather than a specific type derived from typeof AllMeasurementUnits . In this way, we can broad the usage of this type.

Then we can use this type like this :

AnyMeasurementUnit<{
   onlyLength: ['m','km']
}>

AnyMeasurementUnit<{
   onlyWeight: ['g','kg']
}>

@koodiohto
Copy link
Author

Okay, thank you for your feedback!

I was just thinking that my original suggestion was based on an actual "business logic need" which was solved by this AnyMeasurementsUnit-type. So I had the case that I needed to categorize the measurements units, but then I also needed a type for checking that the measurement unit is a valid measurement unit, but doesn't matter to which "measurement unit category" it belongs to. So it's more a type that "validated that a type is a value in another object's arrays".

So if I would try to make the question setup in my original idea to be more broader, I would probably propose it to be like this (so it wouldn't be coupled to the measurements units context):

const ObjectWithArrayValues = {
  key1: [1,2,3], // possibly more in the future
  key2: ['one', 'two', 'three']
  //possibly more in the future
} as const

type AnyValueInObjectWithArrayValues= typeof ObjectWithArrayValues[keyof typeof ObjectWithArrayValues][number]

But I definitely agree that I am having hard time of formulating this into an understandable question. And I don't know if this makes for a good general use case with TS that is a good assignment in the first place..?

@koodiohto
Copy link
Author

But yeah, when still thinking of your good proposal, so maybe it is better to change the assignment description and task to utilize this "generic" type that can be used in a similar fashion as the Partial type. I'll try to modify this next week. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new-challenge Propose a new challenge, a PR will be auto generated
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants