Skip to content

Instantly share code, notes, and snippets.

@peterhudec
Created October 10, 2023 11:36
Show Gist options
  • Select an option

  • Save peterhudec/c0fdda0add399c961a29429c92199198 to your computer and use it in GitHub Desktop.

Select an option

Save peterhudec/c0fdda0add399c961a29429c92199198 to your computer and use it in GitHub Desktop.
TypeScript Generics Proposal
type Func = (...args: any) => any
type FP = <T>(...params: [T, T]) => void
type FPA = <T>(a: [T, T]) => void
type FPO = <T>(o: {x: T, y: T}) => void
type FPF = <T extends Func>(f: T, p: Parameters<T>) => void
type FNested = <T>(x: T, y: T) =>
<TT>(x: TT, y: TT, z: [T, TT]) => void
type FRA = <T>() => [T, T]
type FRO = <T>() => {x: T, y: T}
type FRF = <T extends Func>() => {func: T, args: Parameters<T>}
/*
It would be nice if we could do the same with (readonly?) arrays and objects
type A = <T>[T, T]
const a: A = [
'abc',
123,
^^^ Type 'number' is not assignable to type 'string'.(2322)
]
type O = <T>{x: T, y: T}
const o: O = {
x: 'abc',
y: 123,
^ Type 'number' is not assignable to type 'string'.(2322)
}
type AF = <T extends Func>[T, Parameters<T>]
const af: AF = [
(s: string, n: number) => null,
['abc', 'def'],
^^^^^ Type 'string' is not assignable to type 'number'.(2322)
]
type OF = <T extends Func>{func: T, args: Parameters<T>}
const of: OF = {
func: (s: string, n: number) => null,
args: ['abc', 'def'],
^^^^^ Type 'string' is not assignable to type 'number'.(2322)
}
type ANested = <T>[T, <TT>[TT, [T, TT]]]
type ONested = <T>{x: T, y: <TT>{z: [T, TT]}}
*/
type FPParams = Parameters<FP> // [unknown, unknown]
// Would be nice if it was <T>[T, T]
type FPAParams = Parameters<FPA> // [a: [unknown, unknown]]
// Would be nice if it was <T>[a: [T, T]]
type FPOParams = Parameters<FPO> // [o: {x: unknown, y: unknown}]
// Would be nice if it was <T>[o: {x: T, y: T}]
type FPFParams = Parameters<FPF> // [f: Func, p: any]
// Would be nice if it was <T extends Func>[f: T, p: Parameters<T>]
type FNestedParams = Parameters<FNested> // [a: [unknown, {x: {y: [unknown]}}]]
// Would be nice if it was // <T>[a: [T, {x: {y: [T]}}]]
type FRAReturnType = ReturnType<FRA> // [unknown, unknown]
// Would be nice if it was <T>[T, T]
type FROReturnType = ReturnType<FRO> // {x: unknown, y: unknown}
// Would be nice if it was <T>{x: T, y: T}
type FRFReturnType = ReturnType<FRF> // {func: Func, args: any}
// Would be nice if it was <T extends Func>{func: T, args: Parameters<T>}
declare const fp: FP
declare const fpa: FPA
declare const fpo: FPO
declare const fNested: FNested
declare const fra: FRA
declare const fro: FRO
declare const fpf: FPF
declare const frf: FRF
fp('abc', 123)
// ^^^ Type 'number' is not assignable to type 'string'.(2322)
const fpParams: Parameters<FP> = ['abc', 123]
// ^^^ Would be nice to have the same error here
fpa(['abc', 123])
// ^^^ Type 'number' is not assignable to type 'string'.(2322)
const fpaParams: Parameters<FPA> = [['abc', 123]]
// ^^^ Would be nice to have the same error here
const fraRetVal: ReturnType<FRA> = ['abc', 123]
// ^^^ Would be nice to have the same error here
fNested(['abc', {x: {y: [123]}}])
// ^^^ Type 'number' is not assignable to type 'string'.(2322)
const fNestedParams: Parameters<FNested> = [['abc', {x: {y: [123]}}]]
// ^^^ Would be nice to have the same error here
fpo({x: 123, y: 'abc'})
// ^ Type 'string' is not assignable to type 'number'.(2322)
const fpoParams: Parameters<FPO> = [{x: 'abc', y: 123}]
// ^ Would be nice to have the same error here
const froRetVal: ReturnType<FRO> = {x: 'abc', y: 123}
// ^ Would be nice to have the same error here
fpf((s: string, n: number) => null, ['foo', '123'])
// ^^^^^ Type 'string' is not assignable to type 'number'.(2322)
const fpfParams: Parameters<FPF> = [(s: string, n: number) => null, ['foo', '123']]
// ^^^^^ Would be nice to have the same error here
// When the function result is assigned to an untyped variable,
// the relationship between the two tuple items is lost.
const fraResult = fra() // [unknown, unknown]
const a: [string, number] = fraResult
// ^
// Type '[unknown, unknown]' is not assignable to type '[string, number]'.
// Type at position 0 in source is not compatible with type at position 0 in target.
// Type 'unknown' is not assignable to type 'string'.(2322)
// But when assigning the function call directly, the relationship between the two items seems to be preserved
const b: [string, number] = fra() // const fra: <string | number>() => [string | number, string | number]
// ^
// Type '[string | number, string | number]' is not assignable to type '[string, number]'.
// Type at position 0 in source is not compatible with type at position 0 in target.
// Type 'string | number' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'.(2322)
// Same with objects
const froResult = fro() // const fro: <unknown>() => {x: unknown, y: unknown}
const c: {x: string, y: number} = froResult
// ^
// Type '{ x: unknown; y: unknown; }' is not assignable to type '{ x: string; y: number; }'.
// Types of property 'x' are incompatible.
// Type 'unknown' is not assignable to type 'string'.(2322)
const y: {x: string, y: number} = fro() // const fro: <string | number>() => {x: string | number; y: string | number}
// ^
// Type '{ x: string | number; y: string | number; }' is not assignable to type '{ x: string; y: number; }'.
// Types of property 'x' are incompatible.
// Type 'string | number' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'.(2322)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment