我正在嘗試為一個通用表組件撰寫一個型別,該組件接受一個列串列(每列都有一個名稱和型別)和一個需要符合列規定的形狀的行串列。
我的代碼僅在我添加顯式型別時才有效,但如果 typescript 可以為我推斷出這些,我會更喜歡:
enum ColumnType {
text = "text",
numeric = "numeric",
}
interface Props<T extends Record<string, ColumnType>> {
columns: Array<{ name: keyof T; columnType: ColumnType }>;
rows: Array<
{
[K in keyof T]: T[K] extends ColumnType.numeric
? { content: number }
: { content: string };
}
>;
}
function consume<T extends Record<string, ColumnType>>(arg: Props<T>): void {}
// missing fields, should not typecheck
consume({
columns: [
{ name: "a", columnType: ColumnType.numeric },
{ name: "b", columnType: ColumnType.text },
],
rows: [{}],
});
// type mismatch, should not typecheck
consume({
columns: [
{ name: "a", columnType: ColumnType.numeric },
{ name: "b", columnType: ColumnType.text },
],
rows: [{ a: { content: "asdf" }, b: { content: 42 } }],
});
// should typecheck, but doesn't
consume({
columns: [
{ name: "a", columnType: ColumnType.numeric },
{ name: "b", columnType: ColumnType.text },
],
rows: [{ a: { content: 42 }, b: { content: "asdf" } }],
});
// works with explicit type argument
consume<{ a: ColumnType.numeric; b: ColumnType.text }>({
columns: [
{ name: "a", columnType: ColumnType.numeric },
{ name: "b", columnType: ColumnType.text },
],
rows: [{ a: { content: 42 }, b: { content: "asdf" } }],
});
有沒有辦法用打字稿來完成這個?
打字稿游樂場
uj5u.com熱心網友回復:
第一步是獲取有關列的更多資訊。我們可以通過使整個陣列成為型別引數來做到這一點。為了確保我們捕獲文字型別,我們將為 props 的名稱和列型別添加一些型別引數,以幫助 TS 推斷這些欄位的文字型別,而不是推斷它們更廣泛的型別。
function consume<T extends Array<{ name: K; columnType: CT }>, K extends PropertyKey, CT extends ColumnType>(arg: Props<T>): void {}
我們還想捕獲元組型別而不是陣列型別,以便更輕松地處理陣列的每個元素。為此,我們可以[] |在型別約束中添加一個:
function consume<T extends [] | Array<{ name: K; columnType: CT }>, K extends PropertyKey, CT extends ColumnType>(arg: Props<T>): void {}
為了將陣列的元素映射到物件中的欄位,需要使用映射型別。
在這里,我們有兩個選擇。
第一個選項是使用as映射型別中的子句將元組的每個元素轉換為欄位。我們只需要過濾元組的索引(我們不需要陣列成員)。為此,我們可以與`${number}`(元組索引實際上表示為型別系統中的字串"0", "1" ... )相交
type Row<T extends Array<{ name: PropertyKey; columnType: ColumnType }>> = {} & {
[K in keyof T & `${number}` as T[K]['name']]: T[K]['columnType'] extends ColumnType.numeric
? { content: number }
: { content: string };
}
與舊版本的 TS 兼容的另一個選項是索引 withnumber以獲取元組的所有元素的聯合,映射由name屬性產生的聯合,然后獲取每個欄位的型別,使用提取相應的聯合成分`確切的:
type Row<T extends Record<number, { name: PropertyKey; columnType: ColumnType }>> = {} & {
[K in T[number]['name']]: Extract<T[number], { name: K } >['columnType'] extends ColumnType.numeric
? { content: number }
: { content: string };
}
把它們放在一起,我們得到:
enum ColumnType {
text = "text",
numeric = "numeric",
}
type Row<T extends Array<{ name: PropertyKey; columnType: ColumnType }>> = {} & {
[K in keyof T & `${number}` as T[K]['name']]: T[K]['columnType'] extends ColumnType.numeric
? { content: number }
: { content: string };
}
type Props<T extends Array<{ name: PropertyKey; columnType: ColumnType }>> = {
columns: T;
rows: Array<Row<T>>;
}
function consume<T extends [] | Array<{ name: K; columnType: CT }>, K extends PropertyKey, CT extends ColumnType>(arg: Props<T>): void {}
游樂場鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/516217.html
標籤:打字稿打字稿泛型
