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 — Local types by ahejlsberg · Pull Request #3266 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented May 26, 2015

Copy link
Copy Markdown
Member

This PR implements support for local class, interface, enum, and type alias declarations. For example:

function f() {
    enum E {
        A, B, C
    }
    class C {
        x: E;
    }
    interface I {
        x: E;
    }
    type A = I[];
    let a: A = [new C()];
    a[0].x = E.B;
}

Local types are block scoped, similar to variables declared with let and const. For example:

function f() {
    if (true) {
        interface T { x: number }
        let v: T;
        v.x = 5;
    }
    else {
        interface T { x: string }
        let v: T;
        v.x = "hello";
    }
}

The inferred return type of a function may be a type declared locally within the function. It is not possible for callers of the function to reference such a local type, but it can of course be matched structurally. For example:

interface Point {
    x: number;
    y: number;
}

function getPointFactory(x: number, y: number) {
    class P {
        x = x;
        y = y;
    }
    return P;
}

var PointZero = getPointFactory(0, 0);
var PointOne = getPointFactory(1, 1);
var p1 = new PointZero();
var p2 = new PointZero();
var p3 = new PointOne();

Local types may reference enclosing type parameters and local class and interfaces may themselves be generic. For example:

function f1() {
    function f() {
        class C<X, Y> {
            constructor(public x: X, public y: Y) { }
        }
        return C;
    }
    let C = f();
    let v = new C(10, "hello");
    let x = v.x;  // number
    let y = v.y;  // string
}

function f2() {
    function f<X>(x: X) {
        class C<Y> {
            public x = x;
            constructor(public y: Y) { }
        }
        return C;
    }
    let C = f(10);
    let v = new C("hello");
    let x = v.x;  // number
    let y = v.y;  // string
}

function f3() {
    function f<X, Y>(x: X, y: Y) {
        class C {
            public x = x;
            public y = y;
        }
        return C;
    }
    let C = f(10, "hello");
    let v = new C();
    let x = v.x;  // number
    let y = v.y;  // string
}

Fixes #3217.
Fixes #2148.

Sorry, something went wrong.

Uh oh!

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

All reactions
Anders Hejlsberg (ahejlsberg) added 9 commits May 23, 2015 11:41
@ahejlsberg
Single function to parse statements and module elements
af8aefd
@ahejlsberg
Consistent formatting of optional argument comments
375516e
@ahejlsberg
Allow local interface, type alias, and enum declarations
df9378e
@ahejlsberg
Validate that only module level declarations have modifiers
cf40696
@ahejlsberg
Support local generic types within generic classes and functions
10e940a
@ahejlsberg
Accepting new baselines
142605c
@ahejlsberg
Adding comments
e053cb8
@ahejlsberg
Local types not in scope in parameter lists and return types
f957427
@ahejlsberg
Adding tests
5eff0a5
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label May 26, 2015
DanielRosenwasser
DanielRosenwasser reviewed May 26, 2015
View reviewed changes
Comment thread src/compiler/checker.ts Outdated

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) May 26, 2015

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'd rather this be split into more ifs than cram the conditions into one boolean expression.

Alternatively, break each subexpression onto its own line and explain each of them independently. For instance:

// We've found something in the 'locals' table, but we are sensitive to location in whether it is valid to use
// this entity.
//    - A non-type entity (i.e. values, namespaces, and aliases) is unconditionally resolved.
//    - A type parameters are visible throughout the entirety of a declaration, and are resolved.
//    - A local type found in any declaration *other than a function* can be considered resolved.
//    - A local types found in a function is only resolved if it was used *within the body of the function*.
if (!(meaning & SymbolFlags.Type) ||
    !(result.flags & (SymbolFlags.Type & ~SymbolFlags.TypeParameter)) ||
    !isFunctionLike(location) ||
    lastLocation === (<FunctionLikeDeclaration>location).body) {

Sorry, something went wrong.

Uh oh!

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

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) May 26, 2015

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.

In general I think it is perfectly fine (and often preferable) to have multi-line boolean expressions instead of multiple if statements with "explaining variables". The latter just make the code even more complicated to look at. There's nothing inherently simpler about multiple if statements compared to multiple boolean operands separated by && and ||. Quite the opposite actually, since with boolean expressions you know there aren't any side effects or other oddities you have to worry about.

I do agree that in this case putting each condition on a separate line might make sense.

Sorry, something went wrong.

Uh oh!

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

All reactions

@CyrusNajmabadi Cyrus Najmabadi (CyrusNajmabadi) May 26, 2015

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.

I disagree. I think that multiple if statements are actually simpler. They allow you to examine each clause, one at a time. They let each clause be simple explained. They do not require you to think through multiple different forms of boolean expressions in each clause.

Some minds (mine included) tend to have the problem where we see one expression, and then 'stamp' what we saw there on the next in a complicated clause. So if one expression does || and the next does &&, or one uses ! while the other does not, then it's very easy to 'trip' over the expression, forcing the need to go back and scan and understand the expression over and over again.

At this point, we've been using this code for around a year at this point, and there are still comments/complaints on the PRs about the clarity of the code. At this point, i think we should accept that these constructs are difficult for many team members to read, and we should err on the side of making the code as maintainable as possible for the team as a whole.

Sorry, something went wrong.

Uh oh!

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

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) May 26, 2015

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'll just have to disagree here. If the team has problems reading multi-line boolean expressions then I think we have a bigger problem. You're advocating turning simple and functional boolean expressions into multiple imperative if statements with temporary variables. I think that is exactly the wrong way to go. Whenever I see those I always wonder if I'm missing something subtle (which imperative code is full of), because otherwise it would just be written as a simple boolean expression.

Sorry, something went wrong.

Uh oh!

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

All reactions
JsonFreeman
JsonFreeman reviewed May 28, 2015
View reviewed changes
Comment thread src/compiler/parser.ts Outdated

@JsonFreeman Jason Freeman (JsonFreeman) May 28, 2015

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.

I am confused about the purpose of the getStatementFlags approach. Why is it better than having isStatement and isModuleElement just make the decisions for themselves? Is it so that there can be just one place (instead of two) to account for all the node kinds here, so we don't forget any?

Sorry, something went wrong.

Uh oh!

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

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) May 28, 2015

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.

The problem is modifiers. When modifiers are present we can't tell from the first token what we're looking at. So we need to look ahead, but we don't want to do that more than once. And, as you say, when everything is in one place we're certain that there won't be any subtle differences.

Sorry, something went wrong.

Uh oh!

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

All reactions
@ahejlsberg
Addressing more CR feedback
26b955a
@JsonFreeman

Jason Freeman (JsonFreeman) commented May 30, 2015

Copy link
Copy Markdown
Contributor

In terms of the parser, I prefer the approach where we indiscriminately parse statements and module elements, and subsequently error in the checker if you have a module element in a block context. Since we may eventually allow module declarations inside functions, that work may prove useful to us anyway. But I defer to you, and otherwise, I sign off.

👍

All reactions

Sorry, something went wrong.

Uh oh!

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

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented May 30, 2015

Copy link
Copy Markdown
Member

Since we've moved from typeParameters to innerTypeParameters, what is the interaction with signature help now?

For instance:

function f<FT>(x: FT) {
    class C<CT> {
        private y: CT;
    }

    return C;
}

let con = f(10);
let obj = new con</**/

Do we only see inner type params? This would be a good fourslash test.

All reactions

Sorry, something went wrong.

Uh oh!

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

Anders Hejlsberg (ahejlsberg) added 5 commits May 30, 2015 09:17
@ahejlsberg
Display nested generic types as f<A>.g<B>.C<X, Y>
3f99b74
@ahejlsberg
New test and baselines
143fd5d
@ahejlsberg
Removing unnecessary logic
db30e57
@ahejlsberg
Merge branch 'master' into localTypes
a2783ed
@ahejlsberg
Accepting new baselines
f0ac90f
JsonFreeman
JsonFreeman reviewed May 30, 2015
View reviewed changes
Comment thread src/compiler/checker.ts

@JsonFreeman Jason Freeman (JsonFreeman) May 30, 2015

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.

Is this for anonymous class expressions?

Sorry, something went wrong.

Uh oh!

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

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) May 30, 2015

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.

No, it's for the anonymous type created for the static side of a class.

Sorry, something went wrong.

Uh oh!

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

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) May 30, 2015

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.

Ah yes, because previously the static side of a class could never get instantiated.

Sorry, something went wrong.

Uh oh!

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

All reactions
@JsonFreeman

Jason Freeman (JsonFreeman) commented May 30, 2015

Copy link
Copy Markdown
Contributor

Writing the types looks good 👍

All reactions

Sorry, something went wrong.

Uh oh!

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

@JsonFreeman

Jason Freeman (JsonFreeman) commented May 30, 2015

Copy link
Copy Markdown
Contributor

Daniel Rosenwasser (@DanielRosenwasser) We do not have signature help for type arguments, only arguments. However, we do show the type arguments when we are giving sig help for the arguments, so this would be a good thing to test.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ahejlsberg
Addressing CR feedback
6f734d6
Anders Hejlsberg (ahejlsberg) added a commit that referenced this pull request May 31, 2015
@ahejlsberg
Merge pull request #3266 from Microsoft/localTypes
0872ed6
Local types
@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit 0872ed6 into master May 31, 2015
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the localTypes branch May 31, 2015 00:49
@sophiajt Sophia J. Turner (sophiajt) mentioned this pull request Jun 11, 2015
Suggestion: Anonymous/Unnamed Modules #206
Closed
@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Jun 17, 2015
Proposal: Conditional Compilation #3538
Closed
@danquirk Dan Quirk (danquirk) mentioned this pull request Jul 23, 2015
Generic meta types #3779
Closed
@xLama JCLama (xLama) mentioned this pull request Aug 26, 2015
Proposal. Internal class declaration. #4460
Closed
@rotemdan Rotem Dan (rotemdan) mentioned this pull request Aug 26, 2015
Proposal: structural overloading for interfaces #4302
Closed
@yortus Troy Gerwien (yortus) mentioned this pull request Jun 13, 2016
Allow abstract class for local declarations and expressions #9110
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jun 19, 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.

Allow enums in local scopes Allow local interface definition

Uh oh!

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

7 participants

@ahejlsberg @JsonFreeman @DanielRosenwasser @billti @CyrusNajmabadi @RyanCavanaugh @msftclas
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.