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

add a constraint for immutable types #14909

Open
zpdDG4gta8XKpMCd opened this issue Mar 29, 2017 · 7 comments
Open

add a constraint for immutable types #14909

zpdDG4gta8XKpMCd opened this issue Mar 29, 2017 · 7 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@zpdDG4gta8XKpMCd
Copy link

zpdDG4gta8XKpMCd commented Mar 29, 2017

say, i have a generic function that by design requires that its parameters may only take immutable arguments, i wish i could get such guarantee from TS by declaring my function as follows:

function doThings<readonly T>(value: T): Result<T> {
   // ...
}

as far as how to make an interface immutable:

readonly interface Point {
   readonly x: number;
   readonly y: number;
}

so the problem being solve here is to make sure that the caller won't mutate the argument after it is passed to the function

simple use case: a hash-based data container, which calculates a hash of a given value, and will store it at that hash, and it all will work until the value is mutated outside and tha stored hash is no longer valid, so the container doesn't really work anymore

another use case: a cache or object pool or any other situation when there are many parties involved in taking hold of the same object, which must be guaranteed from being mutated by one party to prevent spooky action at a distance for all other parties

@mhegazy
Copy link
Contributor

mhegazy commented Mar 29, 2017

Readonly<T> should give you a shallow readonly support. For the deep readonly, #10725 already tracks adding that.

@mhegazy mhegazy added the Duplicate An existing issue was already created label Mar 29, 2017
@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented Mar 29, 2017

i was talking about special generic constraints that would guarantee that a type argument is immutable

what did you mean by Readonly?

interface Writable {
    value: string;
}
function willOnlyTakeReadonly<T>(data: Readonly<T>): void {
}
declare const writable: Writable ;
willOnlyTakeReadonly(writable); // <-- no problem

i am afraing it doesn't work at least in the latest version of TS, because anything can be used in place of Readonly<T> parameter including interfaces with all writable properties

@gcanti
Copy link

gcanti commented Mar 29, 2017

@Aleksey-Bykov you can't mutate the input though

type Writable = {
  value: string
}

function willOnlyTakeReadonly(data: Readonly<Writable>): void {
  data.value = 'a' // error Cannot assign to 'value' because it is a constant or a read-only property
}

declare const writable: Writable

willOnlyTakeReadonly(writable)

@mhegazy
Copy link
Contributor

mhegazy commented Mar 29, 2017

not sure I understand then, what did you have in mind constraining the Input?

@zpdDG4gta8XKpMCd
Copy link
Author

i added 2 use cases that shed some light on when/why it is useful to constrain the input

@mhegazy mhegazy closed this as completed Apr 21, 2017
@zpdDG4gta8XKpMCd
Copy link
Author

any reason why it was closed?

@mhegazy mhegazy reopened this Apr 22, 2017
@mhegazy mhegazy added Suggestion An idea for TypeScript and removed Duplicate An existing issue was already created labels Apr 22, 2017
@mattmccutchen
Copy link
Contributor

mattmccutchen commented Sep 26, 2017

I support the goals of this proposal. Some design issues:

  1. For the interface modifier that asserts that the object is immutable, overloading the readonly keyword is very confusing (it looks like it already confused mhegazy). Ideally we should use immutable, though I don't know if there are concerns about adding new reserved words. (I'll use immutable in the rest of my comment.)

  2. What prevents an interface containing the same members without the readonly modifier from being assigned structurally to the Point interface as declared above? Is the immutable modifier of a type one more thing that is checked for assignability?

  3. Users may want mutable, read-only, and immutable versions of the same interface. The current proposal for read-only (Support readonly type operator to allow correct definition of Object.freeze #10725 / Strict variance and read-only versions of types #18770) is that the user defines a mutable interface Point and writes readonly Point for a deep read-only version (perhaps what "deep read-only" means has to be customized in some cases). Similarly, I'd like to write immutable Point for the deep-immutable interface. In essence, the immutable operator should declare fields that form part of the abstract state of the object to be immutable themselves; these are the same fields that become readonly along with the interface. The immutable interface has the same methods as the readonly interface. Given such an operator, the original example could be written as:

function doThings<T>(value: immutable T): Result<T> {
   // ...
}

but additionally having an immutable type parameter constraint may be clearer for users and/or make implementation easier depending on how type argument inference works (yikes).

  1. How do users produce immutable objects? One way is always a type assertion, or a deepFreeze method that uses reflection to find out what fields it should recursively deepFreeze and then asserts the result to be immutable. We could additionally consider ways of constructing a single new immutable object from existing immutable parts, e.g.:
immutable {a: 1, b: 2}
immutable [3, 4, 5]
immutable new MyPair(point1, point2)

To use the same constructor to construct both mutable and immutable objects, we need to make it polymorphic in whether the new object is mutable. I.e., we think of the presence or absence of the immutable modifier on the call as a generic parameter of the constructor, and throughout the constructor's signature and body, the owned type modifier refers to this parameter. Then the compiler checks every assignment to a field to ensure that if the new object is immutable, then the reference being assigned is immutable. (Is anyone aware of precedent for this in other programming languages? I could research it myself, but I've spent enough time on this already.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants