Closed
Description
Suggestion
π Search Terms
const readonly modifier assertion immutable
β 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
Generic functions in TypeScript automatically infer type parameters by its parameters, which is convenient and intelligent.
declare function foo<T>(value: T): T
foo({ a: 1 }) // { a: number }
foo([1, '2']) // (number | string)[]
Sometimes we want to get a more specific inference (asserting some value a is "constant" or "type immutable"):
foo({ a: 1 } as const) // { readonly a: 1 }
foo([1, '2'] as const) // readonly [1, '2']
However, some functions are designed to do such things, which means users should always add "as const" whenever they call it. Can we just place the constant asserting into function declaration?
π Motivating Example
declare function foo<T>(value: const T): T
foo({ a: 1 }) // { readonly a: 1 }
foo([1, '2']) // readonly [1, '2']
Alternative:
// use "readonly" instead of "const"
declare function foo<T>(value: readonly T): T
π» Use Cases
It will be really useful for underlying libraries, such as schema validatation.
declare type Schema<T = any> = (value: T) => T
declare function number(): Schema<number>
declare function string(): Schema<string>
// case 1: tuple type
declare type TypeOf<S> = S extends Schema<infer T> ? T : never
declare type TupleOf<X extends readonly unknown[]> = X extends readonly [infer L, ...infer R] ? readonly [TypeOf<L>, ...TupleOf<R>] :[]
declare function tuple<X extends readonly Schema[]>(list: const X): Schema<TupleOf<X>>
tuple([number(), string()]) // Schema<readonly [number, string]>
// case 2: union type
declare function union<X extends Schema>(list: readonly X[]): Schema<TypeOf<X>>
declare function constant<T>(value: const T): Schema<T>
union([number(), string()]) // Schema<number | string>
union([constant(1), constant('2')]) // Schema<1 | '2'>