- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个从 map 中获取条目的辅助函数,如果它不存在就添加它。
export function mapGetOrCreate<K, V>(map: Map<K, V>, key: K, valueFn: (key: K) => V): V {
let value = map.get(key);
if (value === undefined) {
value = valueFn(key);
assert(value !== undefined);
map.set(key, value);
}
return value;
}
(
Full code in TS playground )
type A = {a: number};
type B = A & {b: string};
type C = B & {c: boolean};
declare function createA(): A;
declare function createB(): B;
declare function createC(): C;
function f(m: Map<string, B>, element: Base) {
m.set('1', createA()); // TS error (good)
m.set('1', createB());
m.set('1', createC());
mapGetOrCreate(m, '1', createA); // missing error!
mapGetOrCreate(m, '1', createB);
mapGetOrCreate(m, '1', createC);
}
我发现将另一个类型参数(
V2
)添加到函数签名“修复”了这个问题:
export function mapGetOrCreate2<K, V, V2 extends V>(map: Map<K, V>, key: K, valueFn: (key: K) => V2): V {
let value = map.get(key);
if (value === undefined) {
value = valueFn(key);
assert(value !== undefined);
map.set(key, value);
}
return value;
}
还是不健全,但至少TypeScript不能自动推断类型,这是一个小小的改进。
mapGetOrCreate2
是否比原始代码更糟糕? 最佳答案
您正在寻找 microsoft/TypeScript#14829 中要求的非推论类型参数用法。对此没有官方支持,但是有许多技术可以实现这种效果,其中之一就是您已经在使用的技术。
为了让以后遇到这个问题的任何人都清楚,这里的不健全之处在于,只要 Map<K, U>
可分配给 Map<K, T>
,TypeScript 就允许将 U
分配给 T
:
function technicallyUnsound<K, T, U extends T>(mapU: Map<K, U>) {
const mapT: Map<K, T> = mapU;
}
只要您只是从 map 中读取,这实际上是完全合理的,但是当您写入它们时,您最终可能会遇到麻烦:
function technicallyUnsound<K, T, U extends T>(mapU: Map<K, U>, k: K, t: T) {
mapU.set(k, u); // error, as desired
const mapT: Map<K, T> = mapU;
mapT.set(k, t); // no error, uh oh
}
const m = new Map<string, Date>();
technicallyUnsound(m, "k", {});
m.get("k")?.getTime(); // compiles okay, but
// RUNTIME ERROR: u is not defined
这就是 TypeScript 的方式;它具有一组有用的功能,例如对象可变性、子类型化、别名和
method bivariance ,这些功能可以提高开发人员的生产力,但可以以不安全的方式使用。无论如何,有关此类健全性问题的更多详细信息,请参阅
this SO answer。
mapGetOrCreate()
,你总是可以使用这样的别名来解决它:
mapGetOrCreate2(m, "", createA) // error, but
const n: Map<string, A> = m;
mapGetOrCreate2(n, "", createA) // no error
但是,考虑到这一点,强化
mapGetOrCreate()
的选项有哪些?
mapGetOrCreate()
遇到的真正问题是 TypeScript 的泛型类型参数推断算法。让我们将
mapGetOrCreate()
归结为一个函数
g()
,我们忘记了键类型
K
(仅使用
string
)。在以下调用中:
declare function g<T>(map: Map<string, T>, valueFn: (key: string) => T): T;
g(m, createA); // no error, not good
// function g<A>(map: Map<string, A>, valueFn: (key: string) => A): A
编译器推断类型参数
T
应该由类型
A
指定,因为
valueFn
返回一个
A
,并且
Map<string, B>
也被认为是有效的
Map<string, A>
。
T
推断
map
,然后检查
valueFn
是否可分配给
(key: string) => T
的
T
。在
valueFn
中,您只想使用
T
而不是推断它。
U
,它是
constrained 到原始类型参数
T
。由于
U
将与
T
分开推断,因此从
T
的角度来看,它看起来是“非推断性的”。这是您对
mapGetOrCreate2
所做的:
declare function g<T, U extends T>(map: Map<string, T>, valueFn: (key: string) => U): T;
g(m, createA); // error! A is not assignable to B
// function g<B, B>(map: Map<string, B>, valueFn: (key: string) => B): B
g(m, createB); // okay
g(m, createC); // okay
另一种非官方方式是
intersect the "non-inferential" locations with {}
,它“降低了该推理站点的优先级”。这不太可靠,仅适用于
X
类型,其中
X & {}
不缩小
X
(因此
X
中不能包含
undefined
或
null
),但它也适用于这种情况:
type NoInfer<T> = T & {};
declare function g<T>(map: Map<string, T>, valueFn: (key: string) => NoInfer<T>): T;
g(m, createA); // error! A is not assignable to type NoInfer<B>
// function g<B>(map: Map<string, B>, valueFn: (key: string) => NoInfer<B>): B
g(m, createB); // okay
g(m, createC); // okay
我知道的最后一种方法是使用
conditional types 的评估是
deferred for as-yet unresolved type parameters 的事实。所以
NoInfer<T>
最终会计算为
T
,但这会在类型推断发生后发生。它也可以在这里工作:
type NoInfer<T> = [T][T extends any ? 0 : never];
declare function g<T>(map: Map<string, T>, valueFn: (key: string) => NoInfer<T>): T;
g(m, createA); // error! A is not assignable to type B
// function g<B>(map: Map<string, B>, valueFn: (key: string) => B): B
g(m, createB); // okay
g(m, createC); // okay
所有这三种方法都是变通方法,并不适用于所有情况。如果你对它的细节和讨论感兴趣,你可以通读 ms/TS#14829。我的主要观点是,如果您的技术适用于您的用例,那么它可能没问题,而且我不知道有任何明显优越的技术。
关于TypeScript:解决 Map `getOrCreate` 辅助函数中的不健全问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67746222/
在我的代码(假设的)中,我想使用 getOrCreate 函数。我传递参数并获取新实体或从数据库获取现有实体(如果存在)。 从一个角度来看,这是一种错误的做法,因为函数不应该做不止一件事。但从另一个角
好的,考虑一下我们大多数人都使用过很多次的常用习语(我假设): class FooBarDictionary { private Dictionary fooBars; ...
我有一个封装与服务器通信的类它有一个 getOrCreate 方法来在服务器中创建 ShoppingCart 资源(我使用的是 firebase,但我的问题是针对所有类型的服务器实现) 一旦应用程序加
在当前实现中 IMemoryCache接口(interface)有以下方法: bool TryGetValue(object key, out object value); ICacheEntry C
我有一个从 map 中获取条目的辅助函数,如果它不存在就添加它。 export function mapGetOrCreate(map: Map, key: K, valueFn: (key: K)
getOrCreate 的目的是什么?来自 SparkContext 的方法类(class)?我不明白我们什么时候应该使用这种方法。 如果我有 2 个使用 spark-submit 运行的 spark
我已经搜索了一段时间,但没有找到我正在尝试做的事情的示例。 我们有一个将被大量使用的 API。其中一项操作是创建一个新的 Client 域对象。每个 Client 的名称都是唯一的。 在下面的代码中,
根据 http://martinfowler.com/bliki/CQRS.html我相信 getOrCreate 函数是一种反模式。 function getOrCreateObj(somethin
SparkContext 类 中的getOrCreate() 方法有什么用,我该如何使用它?我没有为此找到任何合适的示例(编码方式)。 我的理解是,使用上述方法我可以在应用程序之间共享 spark 上
在使用嵌入式 Neo4j (1.8.3) 的 java 应用程序中,我对 org.neo4j.graphdb.index.UniqueFactory.UniqueNodeFactory 进行了子类化,
我将 Spark 2.0 与 PySpark 一起使用。 我正在重新定义 SparkSession参数通过 GetOrCreate 2.0中引入的方法: This method first check
我是一名优秀的程序员,十分优秀!