今天看到一篇关于 noInfer 的文章,特此翻译过来,原文链接 www.totaltypescript.com/noinfer
TypeScript 在5.4版本带来了 NoInfer 新类型,让我们看看它是如何提高在ts在具体条件下的推导能力
TypeScript 5.4 brought a new utility type called
NoInfer. Let's see how it can be used to improve TypeScript's inference behavior in certain situations.
什么是NoInfer
NoInfer是一种可以用来阻止ts从泛型函数内部推导能力的类型
NoInferis a utility type that can be used to prevent TypeScript from inferring a type from inside a generic function.
我们稍后会考虑为什么会这么做,首先我们看一下他是如何工作的
We'll look at why you'd want to do this in a moment. But first, let's see it at work
想象一下我们有一个只返回形参类型的泛型函数
Imagine we have a generic function that just returns the type of the value passed in:
ts
const returnWhatIPassedIn = <T>(value: T) => value;
// hello
const result = returnWhatIPassedIn("hello");
在这个条件下,ts 推断出 result 类型是 hello
In this case, TypeScript infers the type of
resultto be"hello".
但是如果我们使用 NoInfer 包裹呢
But what if we were to wrap
value: TwithNoInfer?
ts
const returnWhatIPassedIn = <T>(value: NoInfer<T>) => value;
// unknow
const result = returnWhatIPassedIn("hello");
NoInfer 阻止了value 从 T 推断出的类型,所以,result 被推断出是 unknown,我们必须显式的提供类型给returnWhatIPassedIn 才能获得想要获取的类型
NoInferhas preventedvaluefrom being a valid source of inference forT. So now,resultis inferred asunknown. We'd need to explicitly provide the type toreturnWhatIPassedInto get the desired return type:
ts
const result = returnWhatIPassedIn<"hello">("hello");
所以,为什么你想要这么做呢?
So, why would you want to do this?
NoInfer 可以解决的问题
想象一下,有一个函数,它有多个变量可以从一个类型推导出来
Let's imagine that you have a function that has multiple candidates for inferring a type parameter.
举一个例子,一个函数创建了一个有限状态机(FSM),这个FSM 有一个 initial 状态和一个 states 列表,这个 initial 必须是 states 的一员
A great example is a function that creates a finite state machine (FSM). The FSM has an
initialstate and a list ofstates. Theinitialstate must be one of thestates.
让我们使用 declare 去定义函数声明(这样我不用提供实现)
Let's use
declareto define the function signature (so I don't have to provide an implementation):
ts
declare function createFSM<TState extends string>(config: {
initial: TState;
states: TState[];
}): TState;
注意到,有两处地方可能倒推出 TState 类型: initial 和 states
Notice that there are two possible places where TypeScript could infer the type
TStatefrom:initialandstates.
如果我们调用他,ts 会从 initial 和 states 两处推断
If we try to call it, TypeScript will infer
TStatefrom BOTHinitialandstates:
ts
const example = createFSM({
initial: "not-allowed",
states: ["open", "closed"],
});
// const example: "not-allowed" | "open" | "closed"
console.log(example);
但是这是一个问题,我们只想从 states 推断出 TState 类型,我们不想从 inital 推断类型,我们想要确保 inital 在 states 数组中
But this is a problem! We want TypeScript to infer
TStatefromstatesonly. We don't wantinitialto be a candidate for inference. We want to ensure thatinitialis a valid state in thestatesarray.
NoInfer 如何帮助解决
我们可以使用 NoInfer 去阻止 initial 作为推断候选
We can use
NoInferto preventinitialfrom being a candidate for inference
ts
declare function createFSM<TState extends string>(config: {
initial: NoInfer<TState>;
states: TState[];
}): TState;
现在,我们调用 createFSM, ts 只会从 states 中推导出 TState 类型,意味着我们正确的从 initial 属性中获取了一个错误
Now, when we call
createFSM, TypeScript will inferTStatefromstatesonly. This means that we correctly get an error from ourinitialproperty:
ts
createFSM({
initial: "not-allowed",
//Type '"not-allowed"' is not assignable to type '"open" | "closed"'.
states: ["open", "closed"],
});
如果你使用 NoInfer 包裹 states,states 只会有一个有效值,那就是 initial
If we swap
NoInferover tostates, then the only validstateswill be the value ofinitial:
ts
declare function createFSM<TState extends string>(config: {
initial: TState;
states: NoInfer<TState>[];
}): TState;
createFSM({
initial: "open",
states: ["closed"],
// Type '"closed"' is not assignable to type '"open"'.
});
所以,我们可以使用 NoInfer 来控制 ts 从函数内部泛型类型,当有多个运行时参数,每个参数都引用相同的类型参数时,这可能很有用。NoInfer 允许你控制ts应该从哪个参数进行推断
So, we can use
NoInferto control where TypeScript infers a type from inside a generic function. This can be useful when you have multiple runtime parameters each referencing the same type parameter.NoInferallows you to control which parameter TypeScript should infer the type from.