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

Design Meeting Notes for 3/4/2016 #7395

Closed
DanielRosenwasser opened this issue Mar 4, 2016 · 0 comments
Closed

Design Meeting Notes for 3/4/2016 #7395

DanielRosenwasser opened this issue Mar 4, 2016 · 0 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

Non-nullable types (#7140)

  • One of the big things we've wanted to do with the type system.
    • A little bit of work left on the PR itself.
  • New compiler switch.
    • --strictNullChecks.
  • Introduces two new (accessible) types called null and undefined.
    • Writing T? is the same as writing T | null | undefined.

Non-immediate assignments

  • JS problem: all variables, by default, have the value undefined:

    var x: number;
    // ...
    x = 1;
    • That's still valid!
    • For that reason, we do some basic assignment-before-use checking.
      • "Rudimentary" at this point.

      • "Perfect" support would require full data-flow analysis.

        • Function hoisting makes this hard.
      • Even Flow doesn't do full data-flow analysis.

      • Though they do control-flow and "type"-flow analysis.

        function (x: string | number) {
            if (typeof x === "string") {
                x = +x;
            }
            // Type of 'x' here is 'number'.
        }
      • Hasn't been a problem so far, but it might start to be a problem for us?

      • Type-flow analysis is in some sense a very general form of data-flow analysis, so...

      • We're going to keep it in mind, but it's not clear what kind of performance implications it would have.

Type guards

  • Standard checks become type guards.

    // Compiled with --strictNullChecks
    declare function f(x: number): string;
    let x: number?;
    if (x) {
        f(x);  // Ok, type of x is number here
    }
    else {
        f(x);  // Error, type of x is number? here
    }
    let a = x != null ? f(x) : "";  // Type of a is string
    let b = x && f(x);  // Type of b is string?
    • x, x == null, x == undefined all have the same behavior.
    • x === undefined and x === null have differing respective behavior.
    • What happens for teams that strictly use null or undefined and use ===/!==?
      • We actually wouldn't be using optionals within the compiler because we only use undefined!
      • We would make a type Maybe<T> = T | undefined.
      • Maybe we should add that and type Nullable<T> = T | null to lib.d.ts.
  • Dotted names in type guards

    interface Options {
        location?: {
            x?: number;
            y?: number;
        };
    
    function foo(options?: Options) {
        if (options && options.location && options.location.x) {
            const x = options.location.x;  // Type of x is number
        }
    }
    • Works on optionals.

Arrays

  • How do we deal with arrays and out-of-bounds?
    • These are simply places we cannot solve the problem.
    • The solution makes things insane to deal with.
    • The cure is worse than the disease.

Behavior of widening and contextual typing

  • Widening is no longer performed when using this new flag.
  • Users now just get the undefined or null types when using these types.
    • So they won't be able to read from them.
    • So they'll have to explicitly opt out.
  • Does that mean we don't need --noImplicitAny?
    • Almost!
    • Empty array literals are still an issue.
      • So --noImplicitAny forces you to decide what to do with empty arrays.
    • Why would they not just become undefined[]?
      • var a: number[] = [];
  • How does this interact with the plan to "contextually type" undefined/null?
    • We were saying this was necessary for contextually implementing class properties.
    • Probably shouldn't be impacted much by this.

Non-null assertion operator

  • A new ! postfix expression-level operator.

  • Produces no runtime-dependent code.

    // Compiled with --strictNullChecks
    function validateEntity(e: Entity?) {
        // Throw exception if e is null or invalid entity
    }
    
    function processEntity(e: Entity?) {
        validateEntity(e);
        let s = e!.name;  // Assert that e is non-null and access name
    }
    • Why can't a type predicate take care of that?
    • It could but it's just cumbersome for some scenarios.

Optionals

  • Optional properties and parameters have fairly intuitive behavior.

    function foo(x?: number) {
    
    }
    • That implicitly gives x the type number | undefined.
  • What about default parameters?

    function foo(x = 15) {
    
    }
    • Externally, x is number | undefined.
      • Declaration files will reflect this.
    • Internally, x has type number.
      • It's as if you had implicitly done the checking you needed (e.g. if (x === undefined) { x = 15; }).
      • Same work has been done for destructuring defaults as well.

Best common type issues

function f(x: boolean) {
    if (x) {
        return 100;
    }
    return undefined;
}
  • There's no best common type between undefined and number, so this caused an error.
  • This is a special case we need to start considering.
    • Enum member types may need this if we choose to adopt them.

Adoption issues

  • Non-null checks are an all-or-nothing sort of feature.
  • Tons of declaration files have been written without this feature in mind (because, well, they couldn't).
  • Becomes a game of wack-a-mole for consumers in patching up declaration files.
  • We could consider a file-specific understanding, or use a tri-state of nullability, but this actually becomes a mental burden for both users and ourselves.
  • Why does this get a switch but not readonly?
    • Meaningful work we can do with readonly that's backwards compatible.
      • Old code doesn't change semantics.
    • Nullability doesn't really have a good story for backwards compatibility - you're now changing the semantics of the old syntax.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Mar 4, 2016
@mhegazy mhegazy closed this as completed Mar 4, 2016
@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.
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

2 participants