這是我的問題的一個最小示例:
const api = {
cat1: {
a: (x: number) => { console.log(x); },
b: (x: string) => { console.log(x); },
},
cat2: {
c: (x: boolean) => { console.log(x); },
},
}
type API = typeof api;
const callAPI = <
Category extends keyof API,
Name extends keyof API[Category],
>(category: Category, name: Name, param: Parameters<API[Category][Name]>[0]) => {
console.log(`Param to ${name}: ${param}`);
};
// All these work
callAPI("cat1", "a", 5);
callAPI("cat1", "b", "foo");
callAPI("cat2", "c", false);
// All these error (as they should)
callAPI("cat2", "a", 5);
callAPI("cat2", "b", "foo");
callAPI("cat1", "c", false);
callAPI("cat1", "a", "foo");
callAPI("cat1", "b", false);
callAPI("cat2", "c", 5);
TypeScript 游樂場鏈接
我的意圖是第一個引數callAPI是“cat1”或“cat2”。然后,根據該值,第二個引數可以是嵌套在“cat1”或“cat2”中的鍵之一。然后,根據該值,第三個引數是內部函式的第一個引數的型別api.cat1.a(或其他)。
但這會產生錯誤:
Type '{ cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category][Name]' does not satisfy the constraint '(...args: any) => any'.
Type '{ cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category][keyof { cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category]]' is not assignable to type '(...args: any) => any'.
Type '{ cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category][string] | { cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category][number] | { ...; }[Category][symbol]' is not assignable to type '(...args: any) => any'.
Type '{ cat1: { a: (x: number) => void; b: (x: string) => void; }; cat2: { c: (x: boolean) => void; }; }[Category][string]' is not assignable to type '(...args: any) => any'.
我不太確定那是什么意思。
我確實知道,如果我擺脫一層嵌套,它會起作用:
const api = {
a: (x: number) => { console.log(x); },
b: (x: string) => { console.log(x); },
c: (x: boolean) => { console.log(x); },
}
type API = typeof api;
const callAPI = <
Name extends keyof API,
Param extends Parameters<API[Name]>[0]
>(name: Name, param: Param) => {
console.log(`Param to ${name}: ${param}`);
};
// All these work
callAPI("a", 5);
callAPI("b", "foo");
callAPI("c", false);
// All these error (as they should)
callAPI("a", "foo");
callAPI("b", false);
callAPI("c", 5);
TypeScript 游樂場鏈接
或者,如果我擺脫Parameters<T>它的作業原理:
const api = {
cat1: {
a: 5,
b: "foo",
},
cat2: {
c: false,
},
}
type API = typeof api;
const callAPI = <
Category extends keyof API,
Name extends keyof API[Category],
>(category: Category, name: Name, param: API[Category][Name]) => {
console.log(`Param to ${name}: ${param}`);
};
// All these work
callAPI("cat1", "a", 5);
callAPI("cat1", "b", "foo");
callAPI("cat2", "c", false);
// All these error (as they should)
callAPI("cat2", "a", 5);
callAPI("cat2", "b", "foo");
callAPI("cat1", "c", false);
callAPI("cat1", "a", "foo");
callAPI("cat1", "b", false);
callAPI("cat2", "c", 5);
TypeScript 游樂場鏈接
所以總的來說,我不確定原始錯誤訊息是什么意思,我不確定為什么我可以通過洗掉一個Parameters<T>或一個嵌套級別來擺脫它。
uj5u.com熱心網友回復:
TypeScript 并不總是能很好地處理這些嵌套索引的約束。你可以通過宣告一個沒有約束的版本來繞過它Parameters(當應用于非函式型別時,它只會評估為 never):
type ParametersUnconstrained<T> = T extends (...args: infer P) => any ? P : never
const callAPI = <
Category extends keyof API,
Name extends keyof API[Category],
>(category: Category, name: Name, param: ParametersUnconstrained<API[Category][Name]>[0]) => {
console.log(`Param to ${name}: ${param}`);
};
或者你可以添加一個條件型別來提醒 TypeScript 約束:API[Category][Name] extends (...args: any) => any ? API[Category][Name] : never,它對應于Extract<API[Category][Name], (...args: any) => any>:
const callAPI = <
Category extends keyof API,
Name extends keyof API[Category],
>(category: Category, name: Name, param: Parameters<Extract<API[Category][Name], (...args: any) => any>>[0]) => {
console.log(`Param to ${name}: ${param}`);
};
第三種選擇是撰寫一個條件型別來進行索引API:
type IndexAPI<Category, Name> =
Category extends keyof API ? Name extends keyof API[Category] ? API[Category][Name] : never : never
這callAPI看起來像這樣:
const callAPI = <
Category extends keyof API,
Name extends keyof API[Category],
>(category: Category, name: Name, param: Parameters<IndexAPI<Category,Name>>[0]) => {
console.log(`Param to ${name}: ${param}`);
};
游樂場鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/419749.html
標籤:
