type-challenges-solutions icon indicating copy to clipboard operation
type-challenges-solutions copied to clipboard

type-challenges-solutions/en/medium-chainable-options

Open utterances-bot opened this issue 3 years ago • 8 comments

Chainable Options

This project is aimed at helping you better understand how the type system works, writing your own utilities, or just having fun with the challenges.

https://ghaiklor.github.io/type-challenges-solutions/en/medium-chainable-options.html

utterances-bot avatar Jul 16 '22 17:07 utterances-bot

Your solution is harder to read on hover in the IDE.. will probably exhaust it after a few steps

& { ... }
type Index = string | number | symbol

type Chainable<O extends { [key: Index]: unknown} = {}> = {
  option<K extends Index, V>(key: K, value: V): Chainable<{ [key in keyof O | K]: key extends keyof O ? O[key] : V }>
  get(): O
}

while hover over get invocation:

(method) get(): {
    foo: number;
    bar: {
        value: string;
    };
    name: string;
}

igalklebanov avatar Jul 16 '22 17:07 igalklebanov

@igalklebanov you can replace Index with PropertyKey type. It's built-in in TypeScript.

ghaiklor avatar Jul 17 '22 11:07 ghaiklor

This solution cannot pass @ts-expect-error, am i right?

dlehddnjs avatar Aug 29 '22 13:08 dlehddnjs

@dlehddnjs what exactly is the test case?

ghaiklor avatar Aug 29 '22 17:08 ghaiklor

@ghaiklor It's the second test case. It does not allow duplicate keys with same type. This works.

type Chainable<O = {}> = {
  option<K extends string, V>(key: K extends keyof O ? (V extends O[K] ? never: K) : K, value: V): Chainable<Omit<O, K> & { [P in K]: V }>;
  get(): O;
};

We check whether the key is already present and if it is of the same type. If either condition fails, we add it to the object. The return type also needs to be updated so that we remove the previous instance of the key that is being added.

AbePlays avatar Aug 30 '22 03:08 AbePlays

@AbePlays cool, thanks for the update. Yeah, I remember passing those tests back then without the additional check 😄

ghaiklor avatar Aug 30 '22 06:08 ghaiklor

Because it was hard for me to understand each solution.

Here is the @AbePlays commented solution

type Chainable<O = {}> = {
  option<K extends string, V>(
    key: K extends keyof O ? 
      // Key already exist
      (
        V extends O[K]
          // Value type is same of previous one
          ? never
          // Value type is different of previous one
          : K

        ) :
      // Key not exist yet       
      K, 
    value: V
  ): 
  // Use Omit<O,K> instead only O To be sure O[K] value have the type of the last .option passed 
  Chainable<Omit<O,K> & {[P in K] : V}>

  get(): O
}

zecka avatar Nov 14 '22 15:11 zecka

@AbePlays The V extends O[K] check isn't needed to pass all test cases (as of Nov 16 2022).

type Chainable<O = {}> = {
  option<K extends string, V>(key: K extends keyof O ? never : K, value: V): Chainable<Omit<O, K> & { [P in K]: V }>
  get(): O
}

MajorLift avatar Dec 28 '22 20:12 MajorLift