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

feature request - null-conditional operator '?' #15098

Closed
dardino opened this issue Apr 10, 2017 · 5 comments
Closed

feature request - null-conditional operator '?' #15098

dardino opened this issue Apr 10, 2017 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@dardino
Copy link

dardino commented Apr 10, 2017

Scenario
I have an Invoice Class with a property Customer like this:

export class Invoice {
    // [...]
    public Customer: Customer | null | undefined;
    // [...]
}
export class Customer {
    // [...]
    public MainContact: Contact | null | undefined;
    // [...]
}
export class Contact {
    // [...]
    public Name: string;
    // [...]
}

To get the Invoice.Customer.MainContact.Name I need to write something like this:

// 
function GetCliDesc(invoice: Invoice | null | undefined): string | null {
   return (invoice ?
      invoice.Customer ?
         invoice.Customer.MainContact ?
            invoice.Customer.MainContact.Name
   : undefined : undefined : undefined);
}

Proposal:
c# like null-conditional operator ?:

function GetCliDesc(invoice: Invoice | null | undefined) {
   return invoice?.Customer?.Contact?.Name;
}
// inferred return type is T | undefined
// where T is the type of last property in the chain:
function GetCliDesc(invoice: Invoice | null | undefined): string | undefined {
   return invoice?.Customer?.Contact?.Name; // ok
}
function GetCliDesc(invoice: Invoice | null | undefined): string | null {
   return invoice?.Customer?.Contact?.Name; // error with --strictNullChecks true
}
function GetCliDesc(invoice: Invoice | null | undefined): string {
   return invoice?.Customer?.Contact?.Name; // error with --strictNullChecks true
}
function GetCliDesc(invoice: Invoice | null | undefined): string {
   return invoice?.Customer?.Contact?.Name || ""; // ok
}

console.log(GetCliDesc(null)) // undefined
console.log(GetCliDesc({})) // undefined
console.log(GetCliDesc({ Customer: null })) // undefined
console.log(GetCliDesc({ Customer: {} })) // undefined
console.log(GetCliDesc({ Customer: { MainContact: null } })) // undefined
console.log(GetCliDesc({ Customer: { MainContact: {} } })) // undefined
console.log(GetCliDesc({ Customer: { MainContact: { Name: "Jhon Wick" } } })) // "Jhon Wick"

let a: string = invoice?.Customer?.Contact?.Name; // error type 'string | null' is not assignable to type 'string'
let a: string = GetCliDesc(); // error type 'string | null' is not assignable to type 'string' 

let firstName = "Jhon Wick";
console.log(firstName?.length) // 9
let firstName = "";
console.log(firstName?.length) // 0
let firstName = null;
console.log(firstName?.length) // undefined
let firstName = undefined;
console.log(firstName?.length) // undefined

the translated javascript can be something like this:

function GetCliDesc(invoice) {
   return (invoice !== undefined && invoice !== null) ?
        (invoice.Customer !== undefined && invoice.Customer !== null) ?
             (invoice.Customer.MainContact !== undefined && invoice.Customer.MainContact !== null) ?
                  invoice.Customer.MainContact.Name
                  : undefined
             : undefined
        : undefined;
}
var a = (invoice !== undefined && invoice !== null) ?
        (invoice.Customer !== undefined && invoice.Customer !== null) ?
             (invoice.Customer.MainContact !== undefined && invoice.Customer.MainContact !== null) ?
                  invoice.Customer.MainContact.Name
                  : undefined
             : undefined
        : undefined;
@dardino dardino changed the title null-conditional operator '?' feature request - null-conditional operator '?' Apr 10, 2017
@krryan
Copy link

krryan commented Apr 10, 2017

Suggest that you refer to the operator as the ?. operator. Or would you include like mightBeFunction?() in this proposal?

@dardino
Copy link
Author

dardino commented Apr 10, 2017

I think mightBeFunction?() can be and interesting case

@dardino
Copy link
Author

dardino commented Apr 10, 2017

another case can be this:

let dictionary: { [key:string]: Function } = {};
function myfunc(p: string): Function | undefined {
   return dictionary[p];
}
let a = myfunc(p1)?(p2);

@krryan
Copy link

krryan commented Apr 10, 2017

Fair enough. Probably worth including in the proposal then, though. And I guess ?[ could also be a thing.

To me, if you have value: A | null | undefined, value?.prop should have the following mapping: (value: A) => A['prop'], (value: null) => null, (value: undefined) => undefined. Your description would have it always map to undefined, though, as far as I can tell. Checking for null vs. undefined does complicate the JavaScript output, but at least in my project, null values becoming undefined could potentially be seen as a code smell—I could see my team avoiding the use of this kind of operator for that reason. Thoughts?

@sandersn
Copy link
Member

sandersn commented Apr 10, 2017

A null propagation operator is a proposed Ecmascript feature. Fortunately, it looks like it advanced to Stage 1 at the January meeting. Typescript tries not to propose new syntax. It implements Ecmascript standard features when they reach stage 3. In the meantime, discussion of how to type this operator happens at #16.

@sandersn sandersn added Duplicate An existing issue was already created External Relates to another program, environment, or user action which we cannot control. labels Apr 10, 2017
@RyanCavanaugh RyanCavanaugh removed the External Relates to another program, environment, or user action which we cannot control. label Apr 10, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 21, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants