打字稿:参数顺序会影响类型推断吗?
Typescript: Does parameter order affect type inference?
我有以下类型:
type State<C, S> = {
onEnter?: (context: C) => void;
onExit?: (context: C) => void;
transitions?: (context: C) => S | undefined;
};
class FSM<C, S extends Record<keyof S, State<C, keyof S>>> {
constructor(public readonly states: S, public readonly context: C) {
/* ... */
}
}
如果我像这样实例化它们:
const context = { foo: "bar" };
const f = new FSM(
{
a: { transitions: c => "b" }, // ERROR: c is implicitly any
b: { transitions: c => "a" } // ERROR: c is implicitly any
},
context
);
编译器抱怨 c
是隐含的 any
并且它无法解析 S
的类型(它出现为 never
)。显式键入 c
可解决此问题,例如:
a: { transitions: (c:typeof context) => "b" },
b: { transitions: (c:typeof context) => "a" }
这是为什么?它不应该能够从 FSM
构造函数的 context
参数推断出 C
吗?
Wild Ass Guess:FSM
构造函数中参数的顺序重要吗?是不是先解析states
的类型,还不知道context
的类型?如果是这样,有没有办法帮助编译器 "look ahead"?
注意:另一件我很难理解的事情是,如果我用随机类型显式输入 c
- 例如transitions: (c:number)=>"a";
编译器抱怨 c
与上下文类型不匹配。所以编译器知道 c
是什么类型,但它需要我向它证明我也知道它?
如果有多个候选者,参数顺序可能对推断很重要,但事实并非如此。你真正想要的是使用上下文类型,这是编译器根据要分配函数的类型推断参数类型(在本例中)的地方。
我不确定上下文类型何时停止的机制,但如果分配给类型需要其他类型参数,它很可能会停止工作。一个简单的解决方法是通过在与类型参数的交集中的参数类型中指定一个更简单的类型来帮助上下文打字。这将确保我们仍然在 S
中捕获传入的实际类型,但我们为上下文类型提供了一个更简单的路径来推断参数类型:
type State<C, S> = {
onEnter?: (context: C) => void;
onExit?: (context: C) => void;
transitions?: (context: C) => S | undefined;
};
class FSM<C, S extends Record<keyof S, State<C, keyof S>>> {
// Record<string, State<C, keyof S>> added for contextual typing
constructor(public readonly states: S & Record<string, State<C, keyof S>>, public readonly context: C) {
/* ... */
}
}
const context = { foo: "bar" };
const f = new FSM(
{
a: { transitions: (c) => c.foo != "" ? "a" : "b" },
b: { transitions: (c) => "a" },
c: { transitions: (c) => "d" }, // Error, d is not in the state keys
},
context
);
我有以下类型:
type State<C, S> = {
onEnter?: (context: C) => void;
onExit?: (context: C) => void;
transitions?: (context: C) => S | undefined;
};
class FSM<C, S extends Record<keyof S, State<C, keyof S>>> {
constructor(public readonly states: S, public readonly context: C) {
/* ... */
}
}
如果我像这样实例化它们:
const context = { foo: "bar" };
const f = new FSM(
{
a: { transitions: c => "b" }, // ERROR: c is implicitly any
b: { transitions: c => "a" } // ERROR: c is implicitly any
},
context
);
编译器抱怨 c
是隐含的 any
并且它无法解析 S
的类型(它出现为 never
)。显式键入 c
可解决此问题,例如:
a: { transitions: (c:typeof context) => "b" },
b: { transitions: (c:typeof context) => "a" }
这是为什么?它不应该能够从 FSM
构造函数的 context
参数推断出 C
吗?
Wild Ass Guess:FSM
构造函数中参数的顺序重要吗?是不是先解析states
的类型,还不知道context
的类型?如果是这样,有没有办法帮助编译器 "look ahead"?
注意:另一件我很难理解的事情是,如果我用随机类型显式输入 c
- 例如transitions: (c:number)=>"a";
编译器抱怨 c
与上下文类型不匹配。所以编译器知道 c
是什么类型,但它需要我向它证明我也知道它?
如果有多个候选者,参数顺序可能对推断很重要,但事实并非如此。你真正想要的是使用上下文类型,这是编译器根据要分配函数的类型推断参数类型(在本例中)的地方。
我不确定上下文类型何时停止的机制,但如果分配给类型需要其他类型参数,它很可能会停止工作。一个简单的解决方法是通过在与类型参数的交集中的参数类型中指定一个更简单的类型来帮助上下文打字。这将确保我们仍然在 S
中捕获传入的实际类型,但我们为上下文类型提供了一个更简单的路径来推断参数类型:
type State<C, S> = {
onEnter?: (context: C) => void;
onExit?: (context: C) => void;
transitions?: (context: C) => S | undefined;
};
class FSM<C, S extends Record<keyof S, State<C, keyof S>>> {
// Record<string, State<C, keyof S>> added for contextual typing
constructor(public readonly states: S & Record<string, State<C, keyof S>>, public readonly context: C) {
/* ... */
}
}
const context = { foo: "bar" };
const f = new FSM(
{
a: { transitions: (c) => c.foo != "" ? "a" : "b" },
b: { transitions: (c) => "a" },
c: { transitions: (c) => "d" }, // Error, d is not in the state keys
},
context
);