Why isn't typeguard working properly with generic and conditional types?

56 Views Asked by At

I wanted to return a readonly value from a function but then found that Readonly<T> can be assigned to anything of type T which is not what I wanted. Now I am trying to make a generic type which is readonly (only 1st level) such that it cannot be assigned to a non-readonly of same type.

I checked issue 13002 and 13347 with no luck. Then I stumbled upon this answer and tried to modify it like this -

type NonObjectAndFn =
    | null
    | undefined
    | string
    | number
    | boolean
    | symbol
    | bigint

type AnyFunction = (...args: any[]) => any

type Frozen<T> = 
    T extends NonObjectAndFn ? T :
    T extends object | AnyFunction ? FrozenObject<T> :
    T

type FrozenObject<T> = {
    readonly [K in keyof T]: T[K];
}

export function freeze<T>(obj: T): Frozen<T> {
    if(obj == undefined) {                                          // 1st
        return obj;
    } else if(typeof obj == "function" || typeof obj == "object") { // 2dn
        // freeze and proxy then
        return frozenObj as Frozen<T>;
    } else {                                                        // 3rd
        return obj;
    }
}

Issue is typescript is giving error for 1st and 3rd block returns. I can't figure out why this is happening since Frozen<T> should be undefined or null for 1st block and since 2nd block checks for functions and objects everything else in NonObjectAndFn should fall to 3rd block and should satisfy Frozen<T>'s conditions right?

1

There are 1 best solutions below

3
stepeusz On

It's a tricky question, as we have to clarify some things. First of all - why would you try to restrict that assignment? Having some type of T, it's perfectly reasonable to be able to assign to it Readonly<T>, but not the other way around. As I can see, you would like to propagate that Readonlyness to the first variable, but that's just not possible to change that type at this point, so I would approach the problem from a different angle. Why you ever allow that T not to be Readonly? Could you please provide a more detailed explanation of what's going on? Maybe that whole fighting with a type system isn't worth it?