Comments (3)
I actually originally supported an optional third argument, defaultValue
. However, I abandoned it because the expected behavior of such an argument turned out to be slightly more complicated than it seems, and I thought it might lead to surprises.
Here are the different potential behaviors for defaultValue
(each with different nuances, none of which are obviously correct):
- Return
defaultValue
only if the final member expression isnull
orundefined
. (This is what you have proposed.) - Return
defaultValue
only if we attempt to access a property onnull
orundefined
. - Return
defaultValue
if any of the member expressions arenull
orundefined
.
Then, there's the question of whether or not we should return defaultValue
if the final value is null
or undefined
.
The difference between these are pretty subtle and easy to get mixed up. In order to avoid confusion, it would be better for callers to explicitly expand the defaulting logic inline.
Also, we should think of idx
as a temporary workaround until we have the real existential operator. A real existential operator will not support default values, so we should not either.
Happy to discuss further, but closing this for now. Thanks for the proposal, though!
from idx.
In @ppoliani 's example:
const DEFAULT_FRIENDS = [{ name: "Pavlos" }]
idx(props, _ => _.user.friends[0].friends, DEFAULT_FRIENDS)
There are five chances to get undefined
:
props == undefined
props.user == undefined
props.user.friends == undefined
props.user.friends[0] == undefined
props.user.friends[0].friends == undefined
We can pass the fourth argument about how to handle undefined
. There are 3 values for strategies:
'LOOKUP'
'EXACTLY'
'ANY'
LOOKUP
We can define default values for levels:
const DEFAULT_FRIENDS = [{ name: "Pavlos" }]
const defaultValues = {
0: [], // for level 0 to level 3
4: DEFAULT_FRIENDS // for level 4
}
const result = idx(props, _ => _.user.friends[0].friends, defaultValues, 'LOOKUP')
So we can get different default values for different undefined levels:
props == null
? [] // level 0 default value
: props.user == null
? [] // level 1 default value
: props.user.friends == null
? [] // level 2 default value
: props.user.friends[0] == null
? [] // level 3 default value
: props.user.friends[0].friends == null
? [{ name: "Pavlos" }] // level 4 default value
: props.user.friends[0].friends
The 'LOOKUP'
strategy uses inheritance, which means that if you did not provide the default value for that level, it will look up the previous level iteratively until finding a default value. If no default value is provided in the inheritance, it will use undefined
.
// now we only provide level 4 default value
const defaultValues = {
4: DEFAULT_FRIENDS
}
const result = idx(props, _ => _.user.friends[0].friends, defaultValues, 'LOOKUP')
// results
props == null
? undefined // level 0 default value
: props.user == null
? undefined // level 1 default value
: props.user.friends == null
? undefined // level 2 default value
: props.user.friends[0] == null
? undefined // level 3 default value
: props.user.friends[0].friends == null
? [{ name: "Pavlos" }] // level 4 default value
: props.user.friends[0].friends
EXACTLY
When we use 'EXACTLY'
, it will only use the default value in the exact level:
const DEFAULT_FRIENDS = [{ name: "Pavlos" }]
const defaultValues = {
0: [], // only for level 0
4: DEFAULT_FRIENDS // only for level 4
}
const result = idx(props, _ => _.user.friends[0].friends, defaultValues, 'EXACTLY')
props == null
? [] // level 0 default value
: props.user == null
? undefined // level 1 default value
: props.user.friends == null
? undefined // level 2 default value
: props.user.friends[0] == null
? undefined // level 3 default value
: props.user.friends[0].friends == null
? [{ name: "Pavlos" }] // level 4 default value
: props.user.friends[0].friends
It won't look up the default value in other levels.
ANY
We provide only a default value for all undefined
levels:
const DEFAULT_FRIENDS = [{ name: "Pavlos" }]
const result = idx(props, _ => _.user.friends[0].friends, DEFAULT_FRIENDS, 'ANY')
props == null
? [{ name: "Pavlos" }] // level 0 default value
: props.user == null
? [{ name: "Pavlos" }] // level 1 default value
: props.user.friends == null
? [{ name: "Pavlos" }] // level 2 default value
: props.user.friends[0] == null
? [{ name: "Pavlos" }] // level 3 default value
: props.user.friends[0].friends == null
? [{ name: "Pavlos" }] // level 4 default value
: props.user.friends[0].friends
If you don't pass the fourth argument to idx()
, the default strategy would be 'ANY;
, which means that we'll get DEFAULT_FRIENDS
for any undefined
value.
const DEFAULT_FRIENDS = [{ name: "Pavlos" }]
const result = idx(props, _ => _.user.friends[0].friends, DEFAULT_FRIENDS) // Use 'ANY' strategy
How about this proposal?
from idx.
@xareelee Thanks for the detailed proposal here and in claudepache/es-optional-chaining#16. However, I personally think it overcomplicates the problem that the optional chaining / existential operator seeks to solve.
Since idx
is meant to be a polyfill for the ES proposal, I'll defer discussion there. If it gets incorporated into the proposal and makes reasonable progress in review, we can bring it into idx
.
from idx.
Related Issues (20)
- Deprecate? HOT 3
- [TS] v2.5.0 DeepRequired generic breaks input.focus() HOT 3
- Consider to support nullable type in `idx` first argument
- Replace `NonUndefinedOrnNull` with TypeScript standard type `NonNullable` HOT 1
- discussion for setIn implementation? HOT 1
- Typescript types do not work on undefined as first parameter HOT 5
- Upgrade to latest version of Flow HOT 3
- Typescript types do not work on undefined as first parameter (new test case) HOT 4
- Change return value from `T2 | null | undefined` to `T2 | null` and `T2 | undefined`? HOT 1
- Doesn't work with TS compiler HOT 3
- Add support for Map.get in .ts files HOT 2
- Cannot find name 'bigint' HOT 6
- `UnboxDeepRequired doesn't support enum type. HOT 1
- CI is not running test for PRs HOT 1
- Outdated CHANGELOG HOT 1
- New release? HOT 1
- @babel/typescript support HOT 3
- Merge in the macro? HOT 1
- DeepRequiredObject causing issues in Typescript 3.5.1 HOT 2
- Accessing Data on Void HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from idx.