Typescript: Refactoring to convert && chain to optional chain expression

Created on 9 Nov 2019  ·  15Comments  ·  Source: microsoft/TypeScript

interface Foo {
    bar?: {
        baz?: string;
    }
}

declare let foo: Foo | undefined;

[|foo && foo.bar && foo.bar.baz|]

It should be possible to convert that chain into the following:

interface Foo {
    bar?: {
        baz?: string;
    }
}

declare let foo: Foo | undefined;

foo?.bar?.baz;
Committed Refactorings Fix Available Suggestion

Most helpful comment

You know, mostly like this

convertToOptionalChain

All 15 comments

You know, mostly like this

convertToOptionalChain

maybe a code-mode or babel transform that runs throughout a codebase?

@Urigo That could be done too, but I think having this granularity is good because it's not strictly the same semantics. ?. always returns undefined, && keeps the original nullish value.

Also keep in mind that optional chaining returns true on 0 and '' (empty string), normally falsy values in javascript. So running this as a code mod would almost certainly cause unintended bugs.

Also keep in mind that optional chaining returns true on 0 and '' (empty string), normally falsy values in javascript. So running this as a code mod would almost certainly cause unintended bugs.

I'm not sure I really follow. Such code would be rather questionable in the first place, no? If you have an && chain that could be converted to an optional chain, and it returns '' or false as a short-circuit, then the same code would be throwing a TypeError if the value is a non-empty string or true.

I could certainly envision a scenario where code was written that depends on short-circuiting on falsy values. We're eternally stuck with typeof null === 'object' for backward compatibility reasons, after all.

Also there's no TypeError because boolean and string are object-coercible. The subsequent property access would just return undefined and short-circuit.

[|foo && foo.bar && foo.bar.baz|]

What do the “|” mean?

@jineshshah36 [|code|] is the syntax used typescript four-slash tests to mark a selection. Some diagnostic suggestions are not exposed unless they can be applied to the selected code.

All too eager to start using the ?. syntax I managed to play a trick on myself by converting
if (foo && foo.bar && foo.bar.indexOf(someValue) !== -1)
to
if (foo?.bar?.indexOf(someValue) !== -1)
which had quite the opposite behaviour of what was intended heh.

Just one of the (probably more obvious in hindsight) things a script would have to keep in mind :)

There's a question of whether or not nullish coalescing (??) should be supported here as part of the code action.

Also, what the supported patterns are:
Category | Input | Output | Comments
-------|----------|-------|-------
&& property chains: | a && a.b.c && a.b.c.d | a?.b.c?.d |
Ternary checks with property access | a.b ? a.b.c : "hello" | a.b?.c ?? "hello" | Is this one type-driven? What if c is string \| null?
&& property/call chains: | a && a.b.c && a.b.c() | a?.b.c?.() |

Ahhhh, i have some existed work about this. And I'm happy to port here

I’m not sure of the viability of going after library refactorings (as opposed to just JS -> TS), but I think one huge win would be if TS could automatically refactor lodash/get usages and/or ramda/pathOr usages.

if TS could automatically refactor lodash/get usages and/or ramda/pathOr usages.

Good idea. IMO TypeScript cannot provide that but there's outside tools.
eg: https://github.com/HearTao/ts-upgrade

Are there any existed tools or algorithms to compare expression and judge they are equality?
I have some ugly work about it but they cannot work well.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manekinekko picture manekinekko  ·  3Comments

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comments

dlaberge picture dlaberge  ·  3Comments

remojansen picture remojansen  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments