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

Support explicit type compatibility assertion #16605

Closed
vilicvane opened this issue Jun 18, 2017 · 8 comments
Closed

Support explicit type compatibility assertion #16605

vilicvane opened this issue Jun 18, 2017 · 8 comments
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@vilicvane
Copy link
Contributor

Sometimes we may want to explicitly assert whether the type of a expression is compatible with a certain type, but type assertion does not satisfy the type safety we want:

interface SomeEventData {
  foo: number;
  bar: string;
}

this.emit('some-event', {foo: 123} as SomeEventData);

The behavior is desired but it would be nice if we have an elegant way for more secure type checking instead of:

let data: SomeEventData = {foo: 123};

E.g.:

this.emit('some-event', {foo: 123}: SomeEventData);
@gcanti
Copy link

gcanti commented Jun 18, 2017

Even better: make sure that incompatible types will raise a type error

class MyEvent<A> {
  readonly _A: A
  constructor(readonly name: string) {}
}

type Handler<A> = (a: A) => void

declare class MyEventEmitter {
  on<A>(event: MyEvent<A>): (handler: Handler<A>) => void
  emit<A>(event: MyEvent<A>): (a: A) => void
}

interface SomeEventData {
  foo: number
  bar: string
}

const emitter = new MyEventEmitter()
const someEvent = new MyEvent<SomeEventData>('some-event')

emitter.emit(someEvent)({}) // error
emitter.emit(someEvent)({ foo: 'a' }) // error
emitter.emit(someEvent)({ foo: 1, bar: 's' }) // ok

emitter.on(someEvent)(() => {}) // ok
emitter.on(someEvent)((foo: number) => {}) // error
emitter.on(someEvent)(foo => {}) // ok
emitter.on(someEvent)((foo: SomeEventData) => {}) // ok

@vilicvane
Copy link
Contributor Author

@gcanti Looks nice (sharing data type between emit and on)! Though creating a wrapper that persists at runtime does not sound ideal to me, especially when we have a lot of libs adapting to implementations like EventEmitter.

@gcanti
Copy link

gcanti commented Jun 18, 2017

Yeah, maybe a branded type instead of a class?

type MyEvent<A> = string & { _A: A } // branded type, the runtime representation is still a string

function createEvent<A>(name: string): MyEvent<A> {
  return name as any
}

const someEvent = createEvent<SomeEventData>('some-event')

@DanielRosenwasser
Copy link
Member

I cannot find other discussion on this, but I have also thought it would be useful to have a type-assertion like operator that doesn't actually coerce the type of the expression.

This operator:

  1. Still provides a contextual type.
  2. Can be used on any expression.
  3. Has the type of the contained expression (rather than the type being ensured/checked against).

For example:

interface Foo {
    bar(x: string): void;
}

// 'x' has type
//
//    {
//        bar(a: string): void,
//        baz(): number
//    }
//
const x = {
    bar(a) {
      console.log(a.toLowerCase());
    }
    baz() {
      return 100;
    }
} compatibleWith Foo;

@DanielRosenwasser DanielRosenwasser added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript labels Jun 18, 2017
@vilicvane
Copy link
Contributor Author

@gcanti Nice try, but it would still be weird working with existing event emitters. :(

@zpdDG4gta8XKpMCd
Copy link

migrated from #20188

in my code i often use, let's call them, type invariants assertions:

function mustBe<T>(_: () => T): void {}
interface A {
    isThat: true;
    text: string;
}
interface B {
    isThat: false;
    value: number;
}
type C = A | B;
declare const C: C;
mustBe<true>(() => C.isThat); // <-- type error as expected

now i wish i could do type invariant assertions without emitting any useless code, making it a zero cost abstraction:

makesure C.isThat sameas true;

where makesure is a new TS syntax/construct that participates in type checking, but doesn't get emitted

few more examples

makesure typeof c.isThat subtypeof boolean
makesure MyClass subtypeof BaseClass
// ...

@RyanCavanaugh
Copy link
Member

c.f. #7481

@RyanCavanaugh
Copy link
Member

Tracking at #7481 / #26064

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants