Advanced TypeScript Exercises - Answer 7
Pragmatic Maciej

Pragmatic Maciej @macsikora

About: I am Software Developer, currently interested in static type languages (TypeScript, Elm, ReScript) mostly in the frontend land, but working actively in Python also. I am available for mentoring.

Location:
Lublin
Joined:
Mar 25, 2019

Advanced TypeScript Exercises - Answer 7

Publish Date: Mar 13 '20
53 8

The question was about exclusive behavior of the type. The core thing is to understand is the never type. Never is an bottom type, so type which has no values, like an empty set. In other words - we cannot assign anything to the type never.

const x: never = 🕳 // there is no possible value to make compiler happy
Enter fullscreen mode Exit fullscreen mode

We will use never in order to achieve the wanted exclusive type behavior.

Answer 7.1 - type which will allow only for a empty object value

type EmptyObject = {
    [K in PropertyKey]: never
}
Enter fullscreen mode Exit fullscreen mode

EmptyObject type is a mapped type, which for every key has value type never. Every key here is represented by K in PropertyKey as PropertyKey represents all possible keys of objects. We could be using [K in any] with the same effect. In result our type for every key allows the value to be never, therefor there is no possibility to put any key into the object, as we have no instance of never bottom type.

Answer 7.2 - change function type to be exclusive for its argument

type Exclusive<T1, T2 extends T1> = {
    [K in keyof T2]: K extends keyof T1 ? T2[K] : never 
}
function takeSomeTypeOnly<T extends SomeType>(x: Exclusive<SomeType, T>) { return x }
Enter fullscreen mode Exit fullscreen mode

In the solution we again are using mapped type, we say that argument is something which extends SomeType and we put it into Exclusive type level function which is doing the needed transformation. Exactly [K in keyof T2]: K extends keyof T1 ? T2[K] : never, so for every key in T2 we check if this key is also in T1(the wanted type) if it is we pass it, if not we put never, it means that such property will always be invalid.

Alternative version of Exclusive can look like that:

type Exclusive<T1, T2 extends T1> = T2 & Record<Exclude<keyof T2, keyof T1>, never>;
Enter fullscreen mode Exit fullscreen mode

We use Record utility type and Exclude utility type in order to make mapped type with all keys invalid for T1 so with never value type. Joining it with T1 gives us the same result - keys which are not in wanted type have never value.

The full code can be found - The Playground

Thank you Manolo Edge for the good answer and taking the challenge.

This series will continue. If you want to know about new exciting questions from advanced TypeScript please follow me on dev.to and twitter.

Comments 8 total

  • Rahul Kashyap
    Rahul KashyapMar 13, 2020

    Thanks for the answers Maciej.

    However the first solution doesn't complain 😢 if i use this input -

    const shouldFail: EmptyObject = {
        prop: (5 as never) // here we should have compile error 🛑 
    }
    
    • Pragmatic Maciej
      Pragmatic MaciejMar 13, 2020

      Good hack Rahul. Unfortunately you can also do the same for all these solution and hack them. I don't think it is possible to prevent such, in the same way we can hack almost every type by as any as X thing.

      In any way I think the wanted behavior was achieved, even though we can hack it, the hack is explicitly visible in the code.

      But thanks for the comment!

  • Keff
    KeffMar 13, 2020

    Cool stuff! I'm liking the series! It's getting advanced

  • John Ralph Umandal
    John Ralph UmandalMar 19, 2020

    **palmface** I was strictly trying to touch only the line of function takeSomeTypeOnly.

    These challenges are really nice, but please be specific with what we can and cannot do. D:

    // change below function type definition 🔥 in order to allow only strict SomeType value
    I might be the one misunderstanding.

    Thanks anyway! Your exercises keep my brain running

    • Pragmatic Maciej
      Pragmatic MaciejMar 19, 2020

      Thanks for the comment. Sorry if smth was not clear.

    • alextsk
      alextskMay 4, 2020

      you can do it in one line

      function takeSomeTypeOnly<T extends SomeType>(x: SomeType extends T ? T : never)
        { return x }
      
      Enter fullscreen mode Exit fullscreen mode
      • ArthurKa
        ArthurKaSep 2, 2022

        Nice try. Don't work.

        const x2 = { prop: 'a' } as const;
        takeSomeTypeOnly(x2);
        
        Enter fullscreen mode Exit fullscreen mode
  • Anna
    AnnaOct 22, 2020

    thank you, these are advanced indeed 🔥🔥 and super interesting :)

Add comment