GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Union Types by ahejlsberg · Pull Request #824 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Oct 4, 2014

Copy link
Copy Markdown
Member

This is the starting implementation of Union Types as proposed in #805. It includes all parts of the core proposal, but not the "Possible Next Steps" (yet). The implementation reflects the decisions we made in the 10/3 design meeting.

Ryan Cavanaugh (@RyanCavanaugh) The pull request doesn't yet include new test baselines (and thus the Travis build will appear to fail). The deltas all look good, but a number of tests are now incorrect and need to be modified. I'm wondering if I can get you to help out with that.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ahejlsberg
Initial implementation of Union Types
e836fe1
CyrusNajmabadi
CyrusNajmabadi reviewed Oct 5, 2014
View reviewed changes
Comment thread src/compiler/parser.ts

@CyrusNajmabadi Cyrus Najmabadi (CyrusNajmabadi) Oct 5, 2014

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right now it looks like it's not possible to have syntax for an array of union types. That seems like it could be problematic in the future. For example, say you have htis in your .js:

function foo(x: string | number) {
    return [x];
}

This function will have a type that is nonexpressable with the syntax of the language (and thus would be a problem for .d.ts files, as well as anyone who wants to explicitly give typings to things).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Oct 5, 2014

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can write this as Array<string|number>

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@CyrusNajmabadi Cyrus Najmabadi (CyrusNajmabadi) Oct 8, 2014

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. We'll likely have to do that if we don't make additional syntax. As htis is a perfectly reasonable workaround, i'd prefer this approach before adding more syntax. thanks!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ghost Deleted user (ghost) Nov 4, 2014

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to have union type aliases, to keep the length of type names managable:

define number | Array<number> | Matrix<number> = DynamicMatrix;

Other values for the define keyword could be alias, type or class

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ghost Deleted user (ghost) Nov 4, 2014

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine this will fall out as a default feature, based on the type inference system of TypeScript, but I think it bears repeating. Union types should have anonymous interfaces:

class A {
    commonToBoth: string;
    uniqueToA: string;
}

class B {
    commonToBoth: string;
    uniqueToB: string;
}

var either: A | B = new A();

//Valid because there is an anonymous interface containing the common members
either.commonToBoth

The anonymous interface would have the form:

interface `unnamable {
    commonToBoth: string;
}

Or go wild and give this interface a name like:

Intersection[A | B] 
Common[A | B]
Interface[A | B] or
Infc[A | B] //for brevity

This does not have any use case I can think of, but perhaps I'm not thinking hard enough.


Daniel Rosenwasser (@DanielRosenwasser) Yes I do. So consider this an upvote. =)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Nov 4, 2014

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Davide Rubino (@drubino) you mean like in #957? 😃

This is a relatively old conversation at this point - you'll find quite a bit has happened recently with respect to union types, such as #914.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ghost Deleted user (ghost) Nov 4, 2014

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haskell has union types denoted by Either A B. From conversations with members of the Haskell community, I've heard that this feature causes a lot of switch statements, leading to verbose, branching code. I'm not sure how to handle this, but perhaps one could create implicit type checks and blocks that use the order of the types as their listed on the union type. This keeps the syntax light-weight.

Something like:

var x = A | B | C;
for x do {
    ///Handle A
} or {
    ///Handle B
} or {
    //Handle c
}

When combined with the anonymous interface idea, along with syntax to combine or skip blocks if logic can be implemented by common members in certain cases, you might be able enable the user to be quite succinct for common scenarios.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ghost Deleted user (ghost) Nov 4, 2014

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might also be interesting to have a way of defining union types. Perhaps there is one master type that all others can be cast to, and the cast is specified in the definition. The compiler would then handle optimizations where casting can be proven to be unnecessary:

class DynamicMatrix contains number | Array<number> | Matrix<number> {
     super: Matrix<number>
     (x: number): Array<number> =  x => [x];
     (x: Array<number>): Matrix<number> = x => new Matrix([x]);
     (x: Array<number>): number = x => x[0] 
         when x.length === 1
         otherwise throw 'Array to number conversion is not possible';
     (x: Matrix<number>): Array<number> = x => x[0] 
          when x.numberOfRows  = 1
          otherwise throw 'Matrix to Array conversion is not possible';
} 

This is probably a bit more complex than the design team had in mind for union types, but I thought I'd throw it out there all the same.

I believe this, or features like it, would provide us with language constructs that would help to bridge the gap between dynamically typed languages and statically typed ones.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@lucamorelli Luca Morelli (lucamorelli) mentioned this pull request Oct 6, 2014
change in ng-grid.d.ts DefinitelyTyped/DefinitelyTyped#2923
Closed
@ahejlsberg
Narrowing of variable types using typeof/instanceof type guards
d70494f
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 7, 2014

Copy link
Copy Markdown
Member Author

Latest commit adds support for type guards along the lines of "Local Meaning of Union Types" in #805. Some examples of what is now supported:

function foo(x: number|string) {
    if (typeof x === "string") {
        return x.length;  // x has type string here
    }
    else {
        return x + 1;  // x has type number here
    }
}

function isLongString(obj: any) {
    return typeof obj === "string" && obj.length > 100;  // obj has type string after guard
}

function processData(data: string|() => string) {
    var d = typeof data !== "string" ? data() : data;  // d has type string
    // Process string in d
}

class NamedItem {
    name: string;
}

function getName(obj: any) {
    return obj instanceof NamedItem ? obj.name : "unknown";
}

The type of the local variable or parameter may be _narrowed_ in the following situations:

  • In an if statement with a condition that contains a type guard.
  • In a conditional statement with a condition that contains a type guard.
  • In the right operand of an && operation where the left operand contains a type guard.
  • In the right operand of an || operation where the left operand contains a type guard.

A _type guard_ is one of the following:

  • An expression of the form typeof x === "..." or typeof x !== "..." where x is the identifier of a variable or parameter and "..." is a string literal. When the string literal is "string", "number", or "boolean", the type guard either changes the type of x to that primitive type or removes that primitive type from the possible types of x (when x is of a union type). When the string literal is something other than a primitive type name, the type guard removes all primtive types from the possible types of x in cases where the guard is known to be true.
  • An expression of the form x instanceof C where x is the identifier of a variable or parameter and C is a constructor function. When the type guard is known to be true, the type of x is changed to the type of C.prototype.
  • An expression of the form !x where x is a type guard.
  • An expression of the form x && y where one or both of x and y are type guards.
  • An expression of the form x || y where one or both of x and y are type guards.

A type guard for a variable x has no effect if the statements or expressions it guards contain assignments to x. For example:

function foo(x: string|number) {
    if (typeof x === "string") {
        x = x.length;  // Fails, x still of type string|number here
    }
    var n = typeof x === "string" ? x.length : x;  // Ok, n has type string
}

Note that type guards affect types of simple variables and parameters only (e.g. x). They have no effect on the types of property accesses or other expressions (such as x.y and x()). The latter would require complicated aliasing analysis.

Also note that it is possible to defeat a type guard by calling a function that changes the type of the guarded variable. It would quite difficult to exhaustively prove that a variable isn't modified by function calls.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Oct 7, 2014
Spec Preview: Union types #805
Closed
@CyrusNajmabadi

Cyrus Najmabadi (CyrusNajmabadi) commented Oct 7, 2014

Copy link
Copy Markdown
Contributor

The type guard concept looks fantastic. I really like it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented Oct 8, 2014

Copy link
Copy Markdown
Member

So are

"string" === typeof obj

and

"string" !== typeof obj

not type guards?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 8, 2014

Copy link
Copy Markdown
Member Author

Daniel Rosenwasser (@DanielRosenwasser) Regarding "string" === typeof obj, no I don't think those would be type guards. Nor would all the various forms that can be constructed by adding parentheses. I think there is something to be said for keeping the pattern simple and consistent.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Mohamed Hegazy (mhegazy) and others added 6 commits October 7, 2014 22:48
@mhegazy
Support symbol kind for union properties
b8923b3
@mhegazy
add test for quick info
5669f63
@mhegazy
Add support for union properties in goto def
c439ae4
@ahejlsberg
Addressing CR feedback
95584e9
@ahejlsberg
Accepting new baselines
fd5b808
The new baselines all look correct to me, but obviously a number of the
tests need to be updated to reflect union types and the new behavior of
best common type. This commit does not cover that.
@ahejlsberg
Improved type argument inference with union types
3a17b02
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 8, 2014

Copy link
Copy Markdown
Member Author

Latest commit improves type argument inference for union types.

When inferring to a union type, we first infer to those types in the union that aren't naked type parameters. If that produces no new inferences, and if the union type contains a single naked type parameter, we then infer to that type parameter.

To infer from a union type we infer from each of the types in the union.

With these changes the following now works with zero type annotations in the actual code:

declare class Promise<T> {
    static resolve<T>(value: Promise<T>|T): Promise<T>;
    then<U>(onfulfilled?: (value: T) => Promise<U>|U): Promise<U>;
}

var p1 = Promise.resolve("hello");  // p1: Promise<string>
var p2 = Promise.resolve(p1);       // p2: Promise<string>
var p3 = p1.then(i => {             // p3: Promise<number>
    if (true) {
        return Promise.resolve(10);
    }
    else {
        return i.length;
    }
});
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

danquirk
danquirk reviewed Oct 8, 2014
View reviewed changes
Comment thread tests/baselines/reference/arrayBestCommonTypes.types Outdated

@danquirk Dan Quirk (danquirk) Oct 8, 2014

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to confirm, this change is because when BCT finds > 1 possible BCT now it chooses one based on 'an order' (based on the type id internally) rather than the first candidate in the list? This seems innocuous in this case since it's only observable because base, base2 and iface types are empty types, if they have members then iface is chosen today.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Oct 8, 2014

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, BCT without a contextual type is now the same as a union type of the elements, and the consitutent types of a union type is an unordered set.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Anders Hejlsberg (ahejlsberg) added 2 commits October 8, 2014 15:23
@ahejlsberg
Merge branch 'master' into unionTypes
ea4cbbe
@ahejlsberg
Accepting new baselines after merge with master
5c661ba
The tuple type tests from master need to be updated to reflect the new
best common type behavior from union types. This commit simply accepts
the baselines as they are.
danquirk
danquirk reviewed Oct 8, 2014
View reviewed changes
Comment thread tests/baselines/reference/contextualTyping33.errors.txt

@danquirk Dan Quirk (danquirk) Oct 8, 2014

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this came up elsewhere but it sure would be nice to display this in lambda form although we don't have a good way to do that right now. It's not actually specific to union types but it becomes slightly worse with them. Something like this:

Array<() => string>

feels much closer to what people write than

Array<{ (): string }>

And gets worse as you add unions:

Array<() => string | () => number>

vs

Array<{ (): number } | { (): string }>

The object literal with call signatures feels a bit like displaying the compiler's internal representation.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Oct 9, 2014

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would basically have to add parentheses to the type grammar:

() => string
(() => string)[]
(() => string) | (() => number)
((() => string) | (() => number))[]

It wouldn't be that complicated, although we'd have to do a bit of lookahead to disambiguate parentheses from parameter lists in function types.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Anders Hejlsberg (ahejlsberg) added 3 commits October 10, 2014 16:57
@ahejlsberg
Less aggressive subtype reduction in union types
483afea
@ahejlsberg
Merge branch 'unionTypes' of https://github.com/Microsoft/TypeScript …
4e02b9f
…into unionTypes
@ahejlsberg
Accepting new baselines
c9a42c1
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 11, 2014

Copy link
Copy Markdown
Member Author

Latest commit fixes issue where overly eager subtype reduction in union types would cause a union type to become any because of a circular reference.

Also, the compiler now simply uses union types for the results of the ?: and || operators instead of the more complicated best common type scheme we had before.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Mohamed Hegazy (mhegazy) added 4 commits October 10, 2014 17:07
@mhegazy
Handle union properties completions on apparant types
2ce627c
@mhegazy
Add a temporary fix to quick info
4442b45
@mhegazy
Merge branch 'unionTypes' into unionTypesLS
04e5309
Conflicts:
	src/compiler/checker.ts
@mhegazy
Merge pull request #861 from Microsoft/unionTypesLS
eee1602
Lanugage Service support for union types
@jbaron

Peter (jbaron) commented Oct 12, 2014

Copy link
Copy Markdown

Did generate a declaration file for large UI library (Qooxdoo) that had its own OO framework. And of the 3 main things missing to really be able to have a very accurate declaration file (and also use that library to its full potential), you guys already addressed 2 of them in recent weeks. Great job!!

Things that were missing from TypeScript version 1.0 :

  • Protected keyword (done)
  • Union type (almost done I guess)
  • Mixin (can always hope for ;)
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 2 commits October 13, 2014 06:56
@ahejlsberg
Correct contextual typing with union types
83d9aed
@ahejlsberg
Accepting new baselines
a76a418
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Oct 13, 2014

Copy link
Copy Markdown
Member Author

Latest commit corrects contextual typing with union types.

  • When an object literal is contextually typed by a union type U, the contextual type for a property P in the object literal is a union of the types of the P properties from those types in U that has one.
  • When an array literal is contextually typed by a union type U, the contextual type of an element expression is a union of the numeric index signature types from those types in U that has one.
  • When a function expression is contextually typed by a union type U, a contextual call signature is available only if each type in U that provides a contextual call signature provides the same one.

Some examples:

var f: number | (x: string) => number;
f = 5;
f = x => x.length;  // x is of type string

var a: Array<number | (x: string) => number>;
a = [5, x => x.length];  // x is of type string

interface A {
    p: Array<number>;
}

interface B {
    p: Array<(x: string) => number>;
}

var obj: A | B = {
    p: [x => x.length];  // x is of type string
};

This commit also changes array and object literals to never produce a result of the contextual type, but rather to use union types to express the exact shape seen. Without this change the last example above wouldn't work.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) and others added 4 commits October 13, 2014 11:26
@ahejlsberg
Addressing CR feedback
869ee41
@ahejlsberg
Merge branch 'master' into unionTypes
fc842b1
Conflicts:
	src/compiler/checker.ts
	src/compiler/types.ts
	src/services/services.ts
	tests/baselines/reference/assignmentCompatBetweenTupleAndArray.errors.txt
	tests/baselines/reference/bestCommonTypeOfTuple.types
	tests/baselines/reference/bestCommonTypeOfTuple2.types
	tests/baselines/reference/castingTuple.errors.txt
	tests/baselines/reference/contextualTypeWithTuple.errors.txt
	tests/baselines/reference/genericCallWithTupleType.errors.txt
	tests/baselines/reference/indexerWithTuple.types
	tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt
@mhegazy
Merge changes from master in services.ts
4f4f59a
@mhegazy
Merge branch 'master' into unionTypes
f5cd414
Conflicts:
	src/compiler/types.ts
	src/services/services.ts
Anders Hejlsberg (ahejlsberg) added a commit that referenced this pull request Oct 13, 2014
@ahejlsberg
Merge pull request #824 from Microsoft/unionTypes
e22500d
Union Types
@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit e22500d into master Oct 13, 2014
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the unionTypes branch October 13, 2014 23:24
@danquirk Dan Quirk (danquirk) mentioned this pull request Oct 16, 2014
Union types conformance tests #906
Closed
@yahiko00

Yahiko Uzumaki (yahiko00) commented Oct 19, 2014

Copy link
Copy Markdown

I've translated into French the spec preview #805: http://www.developpez.net/forums/d1476332/webmasters-developpement-web/javascript-ajax-typescript-dart/typescript/typescript-unions-types-cours-d-implementation/#post8002985

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Nov 3, 2014
Improve union type teppeis/closure-ts#27
Closed
Expand union type teppeis/closure-ts#11
Closed
CyrusNajmabadi
CyrusNajmabadi reviewed Nov 4, 2014
View reviewed changes
Comment thread src/compiler/core.ts

@CyrusNajmabadi Cyrus Najmabadi (CyrusNajmabadi) Nov 4, 2014

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'distinct'.

'deduplicate' indicates you are mutating in the array in place. 'distinct' is the linq name for getting back the set of unique elements.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Nov 6, 2014
Generic function parameters seem to ignore typecheck #1073
Closed
@iccir Ricci Adams (iccir) mentioned this pull request Nov 10, 2014
Support protocols and typed arrays musictheory/NilScript#31
Closed
@jamestalmage James Talmage (jamestalmage) mentioned this pull request Apr 10, 2015
(auto-) generate a typescript d.ts file for better IDE Support benjamn/ast-types#106
Closed
Jed (jednano) referenced this pull request in postcss/postcss Aug 3, 2015
@ai
Lint code with ESLint
709200a
@olegdunkan olegdunkan mentioned this pull request Jul 14, 2017
Local Meaning of Union Types (safe assignments) #17188
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

11 participants

@ahejlsberg @CyrusNajmabadi @DanielRosenwasser @danquirk @jbaron @yahiko00 @dekajp @mihailik @RyanCavanaugh @JsonFreeman @mhegazy
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.