Skip to content

Type variable escaping its scope becomes any #19345

Closed
@pelotom

Description

@pelotom

TypeScript version 2.5.3

I've been playing around with encoding existential types in TypeScript, which has become much easier due to improvements in type inference (thanks!) Here's an example:

type StackSig<S, A> = {
  init(): S
  push(s: S, x: A): void
  pop(s: S): A
  size(s: S): number
}
 
type Stack<A> = <R>(go: <S>(ops: StackSig<S, A>) => R) => R

const arrayStack = <A>(): Stack<A> =>
  go => go<A[]>({
    init: () => [],
    push: (s, x) => s.push(x),
    pop: s => {
      if (s.length) return s.pop()!
      else throw Error('empty stack!')
    },
    size: s => s.length,
  })

const doStringStackStuff = (stack: Stack<string>) =>
  stack(({ init, push, pop, size }) => {
    const s = init()
    push(s, 'hello')
    push(s, 'world')
    push(s, 'omg')
    pop(s)
    return size(s)
  })

const result = doStringStackStuff(arrayStack())
expect(result).toBe(2)

This all works great; the type of the stack object s is an opaque variable S. However what happens if the type variable escapes the scope in which it's bound?

const s = arrayStack()(ops => ops.init())
s.bogus() // no type error, because `s: any`

What happens right now is that s is quietly inferred to have type any. It seems like it should be something like {} or void; a type with which we can't actually do anything meaningful.

Metadata

Metadata

Assignees

Labels

Breaking ChangeWould introduce errors in existing codeBugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions