Skip to content

Instantly share code, notes, and snippets.

@saschanaz
Last active August 29, 2015 14:05
Show Gist options
  • Save saschanaz/c239b84cab2e8f2a8984 to your computer and use it in GitHub Desktop.
Save saschanaz/c239b84cab2e8f2a8984 to your computer and use it in GitHub Desktop.
Design proposal: Type overriding

Introducing override keyword for this proposal. The keyword name is changeable.

Syntax

var foo: T;
override foo = u; // u of arbitrary type U
override foo: TT; // TT that implements T

declare bar: T2;
override declare bar: V; // arbitrary type V, overriding global type declaration

Semantics

Overriding with assignment

This always works regardless of type compatibility.

var foo = t; // t of type T
override foo = u; // u of arbitrary type U
// `foo` now works as if it is declared as `U` type

var some = 3;
some = 'abc'; // Fails
override some = 'abc'; // Succeeds
some = 'efg'; // Succeeds
some = 5; // Fails
override some: number[] = []; // Succeeds
some = [4, 5, 6]; // Succeeds
some = 'string' // Fails

Overriding without assignment

Type compatibilty checker works reversed here, to allow going lower in type hierarchy.

/*
This succeeds only when U implements T.
*/
var foo: T;
var bar: U; // U implements T; 
override foo: U;
// `foo` now works as if it is declared as `U` type

var other: Blob;
override other: File; // Succeeds
override other: string; // Fails

Block-scoped type overriding

var anything: any = getAnything();
if (typeof anything === 'string') {
  override anything: string;
  
  // `anything` now works as if it is declared as string type
  // The new declaration works only in this block.
  anything.split('');
}
//`anything` still works as `any` type here

Global type overriding

This feature is provided for users to fix wrong or outdated type declarations while keeping existing external declaration files intact. Only one overriding is allowed for each declaration, to prevent collision among multiple overrides from multiple files. Globally overridden types are applied to all lines.

Users will be recommended to override global declarations only in the type libraries for this sole specific purpose.

Global type overriding does not require any type compatibility with originally declared type.

declare foo: T;
override declare foo: U;
override declare foo: V; // Error, the type of foo is already overridden.
override declare foo = 4; // Error, not allowed in ambient type declaration

Use cases

Run-time type checking

Imagine that we are writing TypeScript code to polyfill HTML5.1 createImageBitmap function.

function createImageBitmap(input: any) {
  if (input instanceof HTMLImageElement) {
    var image: HTMLImageElement = input;
    // ...
  }
  else if (input instanceof HTMLVideoElement) {
    var video: HTMLVideoElement = input;
    // ...
  }
  else if (input instanceof HTMLCanvasElement) {
    var canvas: HTMLCanvasElement = input;
    // ...
  }
  // ...
}

override keyword would help here to remove those new variable declarations, while keeping type safety for lines below as much as possible.

function createImageBitmap(input: any) {
  if (input instanceof HTMLImageElement) {
    override input: HTMLImageElement;
    // ...
  }
  else if (input instanceof HTMLVideoElement) {
    override input: HTMLVideoElement;
    // ...
  }
  else if (input instanceof HTMLCanvasElement) {
    override input: HTMLCanvasElement;
    // ...
  }
  // ...
}

Type-casting this

function foo() {
  override this: string;
}

Reusing variables

This code is from jews project.

var time = $('#wrap_container_new .sub_tit_area .sub_data').text().split('/');
var date = new Date(time[0].replace(/\./g, '/'));

// TypeScript compiler will give an error here.
time = /([AP]M)\s*(\d\d):(\d\d)/i.exec(time[1]);
var hh = time[2] | 0;
var mm = time[3] | 0;
if (time[1].toUpperCase() === 'PM') hh += 12;
date.setHours(hh);
date.setMinutes(mm);
return {
    created: date,
    lastModified: undefined
};

This will help the line:

override time = /([AP]M)\s*(\d\d):(\d\d)/i.exec(time[1]);

Overriding a type from lib.d.ts

override declare var MouseEvent: {
    prototype: MouseEvent;
    new(typeArg: string, mouseEventInitDict?: MouseEventInit): MouseEvent;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment