0

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?

TubbyStubby
  • 137
  • 3
  • 13
  • Does using `FrozenObject` as a return type work for you? – wonderflame Jun 27 '23 at 18:16
  • @wonderflame do you mean something like [this](https://www.typescriptlang.org/play?#code/C4TwDgpgBAYgTgewF4QHYHkBGArCBjYAHgBUA+KAXigG8AoKBqOCAQwBMFUAbEKAbQDSUAJaooAawggEAMyjEAugC55ghQG5aAX1q0ZAV1QFhnKDOYQUJUgAoZCBCuIBKFfGRosuAtZr1GzMD6cGL2CJo6tKCQ8hAAzsDE4NBU1FAsKqj6ALaYEHBQOnicCVAAHm6IKBg4+ETE8YnJ5FTmEJYQNmkZUACMhc6axailIE6NSTFUZepAA)? I want typescript to say `x` can't be assigned to `y`. – TubbyStubby Jun 27 '23 at 18:31
  • But at least the errors on returns are gone after using `FrozenObject`. That's just confusing me even more lol. – TubbyStubby Jun 27 '23 at 18:38

1 Answers1

0

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?

stepeusz
  • 82
  • 5
  • 1
    Immutability is useful when you want to prevent modification of static data. To ensure immutability, you can use `Object.freeze` or deep copy/clone it. However, `Object.freeze` silently fails without strict mode, and enabling strict mode may not always be an option. TS allows modifications to `Readonly` when assigned to `T`, but that results in and error or a bug during runtime. try [this](https://tsplay.dev/w86EEm) with and without strict. A friendly suggestion, please post questions or follow-ups in the comments. Thank you! – TubbyStubby Jun 28 '23 at 07:26
  • Yeah, I would love to use the comments, but as you can see I'm new here and someone clever thought that restricting commenting but not answering is the way to go :) Thanks for all the information, but still id doesn't answer my question about different approach to the problem? Why wouldn't you consider it? – stepeusz Jun 28 '23 at 11:19
  • Oh, my bad, I didn't know there was a restriction like that for commenting. Well for the question about different approach, I am not sure what there is. Without making the keys at the root of the object readonly I don't think object can be made immutable. And for some reason generic type, type guard and conditional type are not ablet to determine the correct type. But if I use the type directly it infers correctly like for `Frozen`. – TubbyStubby Jun 28 '23 at 20:54