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

New type for tuples which have mutable properties but can't be mutated with array methods #48465

Open
5 tasks done
haydaralrikabi opened this issue Mar 28, 2022 · 4 comments
Open
5 tasks done
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

@haydaralrikabi
Copy link

Suggestion

As the #6325 has been declined to avoid breaking existing codes; I would suggest that we have a "strictTuple" type that denies those array methods on tuples.

πŸ” Search Terms

tuple methods

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

type StrictTuple<T extends any[]> =
    Omit<T, keyof (any[])> extends infer O ? { [K in keyof O]: O[K] } : never;

πŸ“ƒ Motivating Example

const x: StrictTuple<[number, string]> = [1, ""] // {0: number; 1: string }
x[1] = "okay";
x[0] = 123;
x.push(123); // error!
//~~~~ Property 'push' does not exist on type { 0: number; 1: string; }

πŸ’» Use Cases

At the moment, we avoid using array methods like push, pop, shift, unshift when using tuples.
The suggestion above will give developers the option to keep the array methods on tuples or not.

Reference: https://stackoverflow.com/a/64070080/1883323

@MartinJohns
Copy link
Contributor

See #39522 (comment):

We've opted to not include utility type aliases in the lib unless they're required for declaration emit purposes.

@RyanCavanaugh
Copy link
Member

[X] and readonly [X] seem to cover the use cases fairly well. I would probably suggest

type StrictTuple<T> = Omit<T, "push" | "pop" | "shift" | "unshift" | "sort">;

since things like indexOf, map, every, etc are still presumably useful.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Mar 28, 2022
@RyanCavanaugh RyanCavanaugh changed the title Deny the use of array methods on tuples New type for tuples which have mutable properties but can't be mutated with array methods Mar 28, 2022
@jcalz
Copy link
Contributor

jcalz commented Jun 7, 2022

Cross-referencing #40316

@rotu
Copy link

rotu commented Dec 6, 2023

[X] and readonly [X] seem to cover the use cases fairly well. I would probably suggest

type StrictTuple<T> = Omit<T, "push" | "pop" | "shift" | "unshift" | "sort">;

since things like indexOf, map, every, etc are still presumably useful.

These are useful but they don't need to be on the instance. I would instead stick with a null-prototype array (and optionally lock down the object even more). playground

type StrictTuple<T extends any[]> = {
  [k in keyof T]: k extends ('length' | number) ? T[k] : never
}

function makeStrictTuple<T extends any[]>(x: readonly [...T]):StrictTuple<T> {
  // don't add any properties from the prototype (like `forEach` or `constructor`)
  const res = Object.setPrototypeOf([...x], null)
  // can't change the length or add new properties whose keys are natural numbers
  Object.defineProperty(res, 'length', { writable: false })
  // can't delete, redefine, or add new properties at all (but doesn't prevent setting length)
  Object.seal(res)
  return res
}

const tup = makeStrictTuple([1, "a", true])

// Note you can still use Array methods, but they are not properties of the Tuple type:
console.log(Reflect.apply(Array.prototype.indexOf, tup, ['a']))

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