Skip to content

Breaking changes associated with Union Types #868

Closed
@danquirk

Description

@danquirk

This issue tracks the set of breaking changes that result from the change to union types #824 to implement the suggestion #805. The issues here will be formalized further once an official spec update is done, until then it will mostly serve as an easy reference for the type of coding patterns that have broken for various reasons.

Multiple Best Common Type Candidates

Given multiple viable candidates from a Best Common Type computation we now choose an item (depending on the compiler's implementation) rather than the first item.

var a: { x: number; y?: number };
var b: { x: number; z?: number };

// was { x: number; z?: number; }[]
// now { x: number; y?: number; }[]
var bs = [b, a]; 

This can happen in a variety of circumstances. A shared set of required properties and a disjoint set of other properties (optional or otherwise), empty types, compatible signature types (including generic and non-generic signatures when type parameters are stamped out with any).

Recommendation
Provide a type annotation if you need a specific type to be chosen

var bs: { x: number; y?: number; z?: number }[] = [b, a];

Generic Type Inference

Using different types for multiple arguments of type T is now an error, even with constraints involved:

declare function foo<T>(x: T, y:T): T;
var r = foo(1, ""); // r used to be {}, now this is an error

With constraints:

interface Animal { x }
interface Giraffe extends Animal { y }
interface Elephant extends Animal { z }
function f<T extends Animal>(x: T, y: T): T { return undefined; }
var g: Giraffe;
var e: Elephant;
f(g, e);

See #824 (comment) for explanation.

Recommendations
Specify an explicit type parameter if the mismatch was intentional:

var r = foo<{}>(1, ""); // Emulates 1.0 behavior
var r = foo<string|number>(1, ""); // Most useful
var r = foo<any>(1, ""); // Easiest
f<Animal>(g, e);

or rewrite the function definition to specify that mismatches are OK:

declare function foo<T,U>(x: T, y:U): T|U;
function f<T extends Animal, U extends Animal>(x: T, y: U): T|U { return undefined; }

Generic Rest Parameters

You cannot use hetergeneous argument types anymore:

function makeArray<T>(...items: T[]): T[] { return items; }
var r = makeArray(1, ""); // used to return {}[], now an error

Likewise for new Array(...)

Recommendations
Declare a back-compat signature if the 1.0 behavior was desired:

function makeArray<T>(...items: T[]): T[];
function makeArray(...items: {}[]): {}[];
function makeArray<T>(...items: T[]): T[] { return items; }

Overload Resolution with Type Argument Inference

var f10: <T>(x: T, b: () => (a: T) => void, y: T) => T;
var r9 = f10('', () => (a => a.foo), 1); // r9 was any, now this is an error

Recommendations
Manually specify a type parameter

var r9 = f10<any>('', () => (a => a.foo), 1);

Promises

This used to not have an error:

interface Promise<T> {
    then<U>(success?: (value: T) => Promise<U>, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>;
    then<U>(success?: (value: T) => Promise<U>, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>;
    then<U>(success?: (value: T) => U, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>;
    then<U>(success?: (value: T) => U, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>;
    done<U>(success?: (value: T) => any, error?: (error: any) => any, progress?: (progress: any) => void): void;
}

interface IPromise<T> {
    then<U>(success?: (value: T) => IPromise<U>, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>;
    then<U>(success?: (value: T) => IPromise<U>, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>;
    then<U>(success?: (value: T) => U, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>;
    then<U>(success?: (value: T) => U, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>;
    done? <U>(success?: (value: T) => any, error?: (error: any) => any, progress?: (progress: any) => void): void;
}

declare function f1(x: number): IPromise<number>;
declare function f2(x: number): Promise<number>;
var p: Promise<number>;
var r = p.then(f2, f1, f1).then(f1, f1, f1);
//                                 ^^
// error TS2345: Argument of type '(x: number) => IPromise<number>' is not assignable to parameter of type '(value: IPromise<number>) => IPromise<number>'.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Breaking ChangeWould introduce errors in existing codeBy DesignDeprecated - use "Working as Intended" or "Design Limitation" instead

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions