KEMBAR78
Missing objects: ?. and ?? in JavaScript (BrazilJS 2018) | PDF
Missing objects:
?. and ?? in JavaScript
Daniel Ehrenberg @littledan
Igalia, in partnership with Bloomberg
TC39 delegate
BrazilJS 2018
Optional chaining ?.
for
Safe property access
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
const passportNumbers = people.map(person => person.passport.number)
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
const passportNumbers = people.map(person => person.passport.number)
// ===> TypeError!
// people[1].passport is undefined,
// so it has no number property
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
// Fix:
const passportNumbers = people.map(person => {
if (person.passport) return person.passport.number;
else return undefined;
});
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
// Shorter fix:
const passportNumbers = people.map(person =>
person.passport && person.passport.number);
const people = [
{name: "Dan Ehrenberg,
passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}},
{name: "John Doe",
dateOfBirth: new Date(2000, 5, 5)}
];
// With optional chaining:
const passportNumbers = people.map(person =>
person.passport?.number);
Avoid repetition
● Repetition with &&
a && a.b && a.b.c && a.b.c.d
● Don't repeat yourself with ?.
a?.b?.c?.d
Falsiness with &&
const a = { b: 0 };
a && a.b && a.b.toString()
// returns 0
if (0) // takes else branch
if (false) // takes else branch
if (undefined) // takes else branch
if (null) // takes else branch
if ("") // takes else branch
// Similar treatment from &&, ||, etc
0 && 1 // ⇒ 0
false && 1 // ⇒ false
undefined && 1 // ⇒ undefined
null && 1 // ⇒ null
"" && 1 // ⇒ ""
true && 1 // ⇒ 1
Avoid falsiness with ?.
let a = { b: 0 };
a?.b?.toString()
// returns "0"
Exerpted from Wikipedia:
https://en.wikipedia.org/wiki/Safe_navigation_operator
Usable in:
● Coffeescript
● Groovy
● C#
● PHP
● Kotlin
● Angular Templates
● Ruby
● Perl 6
● Swift
● And maybe soon,
JavaScript!
Credit: Oskari Noppa in https://github.com/tc39/proposal-optional-chaining/issues/69#issuecomment-409045384
Nullish coalescing ??
for
default options
Source: Matthew Zeunert, https://www.codereadability.com/what-are-javascript-options-objects/
function findTimeout(options) {
if (options.timeout)
return options.timeout;
else
return 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
function findTimeout(options) {
// Popular shorthand
return options.timeout || 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
function findTimeout(options) {
if (options.timeout)
return options.timeout;
else
return 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
function findTimeout(options) {
if (options.timeout)
return options.timeout;
else
return 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
findTimeout({ timeout: 0 }); // ⇒ timeout ===
function findTimeout(options) {
// Popular shorthand
return options.timeout || 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
findTimeout({ timeout: 0 }); // ⇒ timeout ===
if (0) // takes else branch
if (false) // takes else branch
if (undefined) // takes else branch
if (null) // takes else branch
if ("") // takes else branch
// Similar treatment from &&, ||, etc
undefined || 1 // ⇒ 1
null || 1 // ⇒ 1
0 || 1 // ⇒ 1
"" || 1 // ⇒ 1
false || 1 // ⇒ 1
true || 1 // ⇒ true
function findTimeout(options) {
if (options.timeout !== undefined)
return options.timeout;
else
return 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1
findTimeout({ timeout: 0 }): // ⇒ timeout === 0
function findTimeout(options) {
if (typeof options.foo !== 'undefined')
return options.timeout;
else
return 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1
findTimeout({ timeout: 0 }): // ⇒ timeout === 0
function findTimeout(options) {
return options.foo ?? 100;
}
findTimeout({}); // ⇒ timeout === 100
findTimeout({ timeout: 1 }); // ⇒ timeout === 1
findTimeout({ timeout: 0 }): // ⇒ timeout === 0
undefined ?? 1 // ⇒ 1
null ?? 1 // ⇒ 1
0 ?? 1 // ⇒ 0
"" ?? 1 // ⇒ ""
false ?? 1 // ⇒ false
?? is the new ||
const response = {
settings: {
duration: 0,
splashScreen: false
}
};
const duration = response.settings?.duration ?? 300; // 0
const splashScreen = response.settings?.splashScreen ?? true; // false
const headerText = response.settings?.headerText ?? "Header"; // "Header"
Works great together
But is all
that JS?
Maybe it will be soon!
We are looking into it
Optional chaining (?.)
& nullish coalescing (??)
are at Stage 1
Champion: Gabriel Isenberg
// ES6 Destructuring Assignment
let x = 1, y = 2;
[y, x] = [x, y];
⇒ x === 2
y === 1
Who is TC39?
● A committee of Ecma, with…
● JS developers
● Implementers
● Frameworks
● Academics
● etc
Meetings
● Every two months
● For three days
● Summarize/review GitHub activity
● Move proposals through stages
TC39 stages
● Stage 1: An idea
● Stage 2: Agreed-on first draft
● Stage 3: Details worked out
● Stage 4: Battle-tested and standard
?. and ?? each reached Stage 1
● The committee expects to discuss it ✔
● Gabriel Isenberg presented them to TC39,
In 2017
Next steps for
?. and ??
Steps needed for Stage 2
● First draft spec text ✔
● Agreement on syntax and semantics …
● Agreement that we want the proposal …
Details:
Method calls and
property access syntax
// Most languages:
obj.method?(args)
obj?[index]
// JS:
obj.method?.(args)
obj?.[index]
// JS:
obj.method?.(args)
obj?.[index]
// Why?
// JS syntax tries to avoid lots of lookahead
// Ambiguity with ternary operator ?:
obj?[index]:bar
obj.method?(args):bar
// Alternate proposal:
obj??.property
obj.method??(args)
obj??[index]
obj ??? default
Details:
Handling of null
// null is treated as missing, like undefined
null ?? x // ⇒ x
(null)?.x // ⇒ undefined
// Handle null as not missing?
Bigger picture:
Do we need a new
feature?
Add these features?
● Pro
○ Makes some code cleaner
○ Avoids bugs from existing idioms
● Con
○ More to learn, mentally parse, etc
Steps needed for Stage 2
● First draft spec text ✔
● Agreement on syntax, broadly …
● Agreement that we want the proposal …
Plan from here
● Experiment with ?. and ?? in Babel, at Stage 1
● Collect feedback over time
● Consider for Stage 2 in the future
● The features are easy to implement; we are just
taking our time to work out the right design.
Steps needed for Stage 3
● Complete spec text …
● Signoff from reviewers, editor …
● Ready to pass off to implementers …
Steps needed for Stage 4
● Two implementations …
● Conformance tests …
● PR with editor signoff …
Getting involved
Feedback on GitHub issues
● Does this work for you?
● Missing pieces?
● Handle a case differently?
?. GitHub feedback
● Syntax: ?. vs ?&. vs ??.
● Null handling
● Which operations support
optional chaining
● And more!
Test262 tests
● Tests shared between JS engines
● “If it’s not tested, it’s not compatible”
● Finds edge cases
?./?? test262 tests
● ...None yet
● Could be useful for Babel development
● Wanna help?
Documentation/Education
● Explain the feature to contributors, learners
● Understand mental models for JS
?./?? documentation
● "Explainer documents" for each proposal
● Wanted: Better introductory materials
● Wanted: Educators' perspective on the proposals
Implementation
● Open-source projects accepting contributions:
○ Babel
○ TypeScript
○ V8
○ Acorn
● Features developed behind a flag/in a plugin
○ JSC
○ ChakraCore
○ SpiderMonkey
○ And many more
?./?? implementations
● Babel transforms in 7.0
○ @babel/plugin-proposal-nullish-coalescing-operator
○ @babel/plugin-proposal-optional-chaining
■ Included in React Native 0.56
○ Thanks, Justin Ridgewell and Lucas Azzola!
● Want to write more implementations? Try this out?
Attending TC39 as a delegate
● Join Ecma to come to TC39 meetings
● Contact me for more info, littledan@igalia.com
● Get involved in TC39:
https://tc39.github.io/
● ?. and ?? are at Stage 1
for handling null/undefined
● Daniel Ehrenberg
● Twitter/GitHub @littledan
● littledan@igalia.com

Missing objects: ?. and ?? in JavaScript (BrazilJS 2018)

  • 1.
    Missing objects: ?. and?? in JavaScript Daniel Ehrenberg @littledan Igalia, in partnership with Bloomberg TC39 delegate BrazilJS 2018
  • 2.
  • 3.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ];
  • 4.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ]; const passportNumbers = people.map(person => person.passport.number)
  • 5.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ]; const passportNumbers = people.map(person => person.passport.number) // ===> TypeError! // people[1].passport is undefined, // so it has no number property
  • 6.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ]; // Fix: const passportNumbers = people.map(person => { if (person.passport) return person.passport.number; else return undefined; });
  • 7.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ]; // Shorter fix: const passportNumbers = people.map(person => person.passport && person.passport.number);
  • 8.
    const people =[ {name: "Dan Ehrenberg, passport: {number: 1234567n, expiration: new Date(2020, 1, 1)}}, {name: "John Doe", dateOfBirth: new Date(2000, 5, 5)} ]; // With optional chaining: const passportNumbers = people.map(person => person.passport?.number);
  • 9.
    Avoid repetition ● Repetitionwith && a && a.b && a.b.c && a.b.c.d ● Don't repeat yourself with ?. a?.b?.c?.d
  • 10.
    Falsiness with && consta = { b: 0 }; a && a.b && a.b.toString() // returns 0
  • 11.
    if (0) //takes else branch if (false) // takes else branch if (undefined) // takes else branch if (null) // takes else branch if ("") // takes else branch // Similar treatment from &&, ||, etc
  • 12.
    0 && 1// ⇒ 0 false && 1 // ⇒ false undefined && 1 // ⇒ undefined null && 1 // ⇒ null "" && 1 // ⇒ "" true && 1 // ⇒ 1
  • 13.
    Avoid falsiness with?. let a = { b: 0 }; a?.b?.toString() // returns "0"
  • 14.
    Exerpted from Wikipedia: https://en.wikipedia.org/wiki/Safe_navigation_operator Usablein: ● Coffeescript ● Groovy ● C# ● PHP ● Kotlin ● Angular Templates ● Ruby ● Perl 6 ● Swift ● And maybe soon, JavaScript!
  • 15.
    Credit: Oskari Noppain https://github.com/tc39/proposal-optional-chaining/issues/69#issuecomment-409045384
  • 16.
  • 17.
    Source: Matthew Zeunert,https://www.codereadability.com/what-are-javascript-options-objects/
  • 18.
    function findTimeout(options) { if(options.timeout) return options.timeout; else return 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
  • 19.
    function findTimeout(options) { //Popular shorthand return options.timeout || 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
  • 20.
    function findTimeout(options) { if(options.timeout) return options.timeout; else return 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1;
  • 21.
    function findTimeout(options) { if(options.timeout) return options.timeout; else return 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1; findTimeout({ timeout: 0 }); // ⇒ timeout ===
  • 22.
    function findTimeout(options) { //Popular shorthand return options.timeout || 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1; findTimeout({ timeout: 0 }); // ⇒ timeout ===
  • 23.
    if (0) //takes else branch if (false) // takes else branch if (undefined) // takes else branch if (null) // takes else branch if ("") // takes else branch // Similar treatment from &&, ||, etc
  • 24.
    undefined || 1// ⇒ 1 null || 1 // ⇒ 1 0 || 1 // ⇒ 1 "" || 1 // ⇒ 1 false || 1 // ⇒ 1 true || 1 // ⇒ true
  • 25.
    function findTimeout(options) { if(options.timeout !== undefined) return options.timeout; else return 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1 findTimeout({ timeout: 0 }): // ⇒ timeout === 0
  • 26.
    function findTimeout(options) { if(typeof options.foo !== 'undefined') return options.timeout; else return 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1 findTimeout({ timeout: 0 }): // ⇒ timeout === 0
  • 27.
    function findTimeout(options) { returnoptions.foo ?? 100; } findTimeout({}); // ⇒ timeout === 100 findTimeout({ timeout: 1 }); // ⇒ timeout === 1 findTimeout({ timeout: 0 }): // ⇒ timeout === 0
  • 28.
    undefined ?? 1// ⇒ 1 null ?? 1 // ⇒ 1 0 ?? 1 // ⇒ 0 "" ?? 1 // ⇒ "" false ?? 1 // ⇒ false ?? is the new ||
  • 29.
    const response ={ settings: { duration: 0, splashScreen: false } }; const duration = response.settings?.duration ?? 300; // 0 const splashScreen = response.settings?.splashScreen ?? true; // false const headerText = response.settings?.headerText ?? "Header"; // "Header" Works great together
  • 30.
  • 31.
    Maybe it willbe soon! We are looking into it Optional chaining (?.) & nullish coalescing (??) are at Stage 1 Champion: Gabriel Isenberg
  • 32.
    // ES6 DestructuringAssignment let x = 1, y = 2; [y, x] = [x, y]; ⇒ x === 2 y === 1
  • 33.
    Who is TC39? ●A committee of Ecma, with… ● JS developers ● Implementers ● Frameworks ● Academics ● etc
  • 36.
    Meetings ● Every twomonths ● For three days ● Summarize/review GitHub activity ● Move proposals through stages
  • 37.
    TC39 stages ● Stage1: An idea ● Stage 2: Agreed-on first draft ● Stage 3: Details worked out ● Stage 4: Battle-tested and standard
  • 38.
    ?. and ??each reached Stage 1 ● The committee expects to discuss it ✔ ● Gabriel Isenberg presented them to TC39, In 2017
  • 39.
  • 40.
    Steps needed forStage 2 ● First draft spec text ✔ ● Agreement on syntax and semantics … ● Agreement that we want the proposal …
  • 41.
  • 42.
    // Most languages: obj.method?(args) obj?[index] //JS: obj.method?.(args) obj?.[index]
  • 43.
    // JS: obj.method?.(args) obj?.[index] // Why? //JS syntax tries to avoid lots of lookahead // Ambiguity with ternary operator ?: obj?[index]:bar obj.method?(args):bar
  • 44.
  • 45.
  • 46.
    // null istreated as missing, like undefined null ?? x // ⇒ x (null)?.x // ⇒ undefined // Handle null as not missing?
  • 47.
    Bigger picture: Do weneed a new feature?
  • 48.
    Add these features? ●Pro ○ Makes some code cleaner ○ Avoids bugs from existing idioms ● Con ○ More to learn, mentally parse, etc
  • 49.
    Steps needed forStage 2 ● First draft spec text ✔ ● Agreement on syntax, broadly … ● Agreement that we want the proposal …
  • 50.
    Plan from here ●Experiment with ?. and ?? in Babel, at Stage 1 ● Collect feedback over time ● Consider for Stage 2 in the future ● The features are easy to implement; we are just taking our time to work out the right design.
  • 51.
    Steps needed forStage 3 ● Complete spec text … ● Signoff from reviewers, editor … ● Ready to pass off to implementers …
  • 52.
    Steps needed forStage 4 ● Two implementations … ● Conformance tests … ● PR with editor signoff …
  • 53.
  • 54.
    Feedback on GitHubissues ● Does this work for you? ● Missing pieces? ● Handle a case differently?
  • 55.
    ?. GitHub feedback ●Syntax: ?. vs ?&. vs ??. ● Null handling ● Which operations support optional chaining ● And more!
  • 56.
    Test262 tests ● Testsshared between JS engines ● “If it’s not tested, it’s not compatible” ● Finds edge cases
  • 58.
    ?./?? test262 tests ●...None yet ● Could be useful for Babel development ● Wanna help?
  • 59.
    Documentation/Education ● Explain thefeature to contributors, learners ● Understand mental models for JS
  • 61.
    ?./?? documentation ● "Explainerdocuments" for each proposal ● Wanted: Better introductory materials ● Wanted: Educators' perspective on the proposals
  • 62.
    Implementation ● Open-source projectsaccepting contributions: ○ Babel ○ TypeScript ○ V8 ○ Acorn ● Features developed behind a flag/in a plugin ○ JSC ○ ChakraCore ○ SpiderMonkey ○ And many more
  • 64.
    ?./?? implementations ● Babeltransforms in 7.0 ○ @babel/plugin-proposal-nullish-coalescing-operator ○ @babel/plugin-proposal-optional-chaining ■ Included in React Native 0.56 ○ Thanks, Justin Ridgewell and Lucas Azzola! ● Want to write more implementations? Try this out?
  • 65.
    Attending TC39 asa delegate ● Join Ecma to come to TC39 meetings ● Contact me for more info, littledan@igalia.com
  • 66.
    ● Get involvedin TC39: https://tc39.github.io/ ● ?. and ?? are at Stage 1 for handling null/undefined ● Daniel Ehrenberg ● Twitter/GitHub @littledan ● littledan@igalia.com