我正在寻找一个客户端JavaScript库,它允许我使用某种类型的Option类型编写类似于我在其他语言中可以执行的代码,例如java.lang.Optional。
我的目标是避免在客户端代码中进行null
/ undefined
检查,并使API更加明确。
以下是我希望能够编写的API:
var dictionary = {
key1: 'value1',
key2: 'value2'
}
function getValue(key){
var value = dictionary[key];
if(value !== null && value !== undefined)
return Optional.of(value);
else
return Optional.empty();
}
这是客户端代码:
var value = getValue('key3');
if(value.isPresent())
console.log('got a value: ' + value.get().toUpperCase());
else
console.log('no value found!');
或者有时候:
var value = getValue('unknown key').orElse('default value');
如果我在get()
值上调用Optional.empty()
,则应该抛出某种Error
。
如果我在Optional.of()
或null
上致电undefined
,它也应该投放。
答案 0 :(得分:3)
鉴于java.lang.Optional source,编写一个可选实现本身不应该那么难。
虽然你必须考虑一些差异,ieJavascript区分undefined
和null
,你没有像Generics这样的概念,并且某些人认为map的Java实现为{{3 }}。然而,它很容易实现。
但是,我自己完成了一项实施(broken),这符合我的需求,如果您愿意,可以使用它,但它可能无法满足您的所有要求(即{ {1}}不会在undefined或null上抛出异常,但of()
会这样做
使用我的代码,您的示例就像
get()
顺便说一下。像这样的代码
function getValue(key){
return Optional.of(dictionary[key]);
}
var value = getValue('key3');
value.ifPresent(function() {
console.log('got a value: ' + value.get().toUpperCase());
});
对于Optional是无效的用例,因为您可以轻松替换它将null / undefined检查。但是,如果您需要if(value.isPresent())
console.log('got a value: ' + value.get().toUpperCase());
else
console.log('no value found!');
,filter
,map
和flatMap
等方法,您将从中受益更多。
答案 1 :(得分:3)
对于那些在 2021 年仍然对此感兴趣的人。
始终使用 ES Optional chain。它在 JavaScript 方式中更具语义和艺术性(也许使用 Babel)。喜欢:
obj.val?.prop || defaultValue
obj.val?.[expr]
obj.arr?.[index]
obj.func?.(args)
使用 Optional
类时,TypeScript 中的其他包可能不支持,如果您只想深入访问对象属性,则不 > 有点容易。
我尝试使用 TypeScript 创建一个大致 Optional
的类:
import isNil from "../is/isNil";
// isNil = val => val === null || val === undefined
class Optional<T> {
value = null;
constructor(value: T) {
this.value = value;
}
static EMPTY = new Optional(null);
static empty(): Optional<unknown> {
return Optional.EMPTY;
}
static of<U>(value: U): Optional<U> {
return new Optional(value);
}
isPresent(): boolean {
return !isNil(this.value);
}
filter<T>(predicate: (value: T) => Boolean): Optional<T> {
if (!this.isPresent()) {
return this;
}
return predicate(this.value) ? this : Optional.EMPTY;
}
map<T, U>(mapper: (value: T) => U): Optional<U> {
if (!this.isPresent()) {
return this;
}
return Optional.of(mapper(this.value));
}
flatMap<T, U>(mapper: (value: T) => Optional<U>): Optional<U> {
if (!this.isPresent()) {
return this;
}
const mapped = mapper(this.value);
if (isNil(mapped)) {
throw new Error("flatMap will map the value not to null or undefined.");
}
return mapped;
}
orElse<T>(other: T): T {
return isNil(this.value) ? other : this.value;
}
}
在这里你可以看到我想使用Optional访问属性的时候,它会写成如下:
Optional.of(dictionary)
.map((obj)=> {
const descriptors = Object.getOwnPropertyDescriptors(obj)
// ⚠️ when you use like this, it may also cause an error.
return descriptors.key1.value
})
.orElse('defaultValue')
使用 Optional chain
会像这样:
dictionary?.key1 || 'defaultValue'
答案 2 :(得分:0)
对于某些使用情况,一个零到一个元素的数组非常合适。
这很简单,具有用于创建和查询的简洁语法,还为您提供了内置的filter()和map(),这是我使用Optional的主要原因:
const one = ["foo"];
const none = [];
console.log("one.isPresent: " + !!one.length);
console.log("none.isPresent: " + !!none.length);
console.log("one.get: " + one[0]);
// Caveat: this 'orElse' will fail for values that are present but falsy:
console.log("one.orElse('fallback'): " + (one[0] || 'fallback') );
console.log("none.orElse('fallback'): " + (none[0] || 'fallback'));
console.log("Fluent chaining: "+ (
none
.filter(s => s.length > 2)
.map(lodash.upperCase)
[0] || "too short"
));
如果您愿意修改原型,则可以根据需要添加更强大的orElse和语法糖。