نوع keyof در TypeScript خیلی وسیع است
ببخشید، مطمئنم که این جایی پاسخ داده شده است، اما مطمئن نیستم چه چیزی را گوگل کنم. لطفاً اگر اصطلاحات عنوان اشتباه هستند، سؤال من را ویرایش کنید.
من چیزی شبیه این دارم:
type RowData = Record<string, unknown> & {id: string};
type Column<T extends RowData, K extends keyof T> = {
key: K;
action: (value: T[K], rowData: T) => void;
}
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<Column<T, keyof T>>;
}
و TypeScript باید قادر باشد نوع توابع action را با بررسی شکل ردیفها و اینکه چه مقدار key به ستون داده شده است، استنتاج کند:
یعنی :
function myFn<T extends RowData>(config: RowsAndColumns<T>) {
}
myFn({
rows: [
{id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a); //Property 'a' does not exist on type 'string | { a: number; b: string; }'.
}
}
]
});
لینک زمین بازی
مشکل این است که به نظر میرسد TypeScript نوع مقدار (value: T[K]) را به عنوان «انواع همه چیزهایی که توسط تمام کلیدهای T قابل دسترسی است» به دست میآورد، به جای اینکه فقط از کلیدی که در شیء ستون ارائه شده استفاده کند.
چرا تایپاسکریپت این کار را انجام میدهد و چگونه میتوانم آن را حل کنم؟
چیزی که میتواند پاسخ خوبی باشد، تعریف برخی از اصطلاحات و مفاهیم خاص است.
من تصور میکنم میخواهم K extends keyof T خود را به چیزی مثل 'K یک keyof از T است، اما فقط یک کلید است و هرگز تغییر نمیکند' تغییر دهم.
پاسخ شماره ۱
اگر انتظار دارید کلیدهای T یک اتحاد از مقادیر ثابت مانند "bong" | "bing" | ... باشند (و نه فقط string)، آنگاه میتوانید نوعی را بیان کنید که خود یک اتحاد از Column<T, K> برای هر کلید K در keyof T باشد.
من معمولاً این کار را از طریق نمایهگذاری فوری (جستجو) در یک نوع نگاشته انجام میدهم:
type SomeColumn<T extends RowData> = {
[K in keyof T]-?: Column<T, K>
}[keyof T]
اما شما همچنین میتوانید این کار را از طریق نوعهای شرطی توزیعی انجام دهید:
type SomeColumn<T extends RowData> = keyof T extends infer K ?
K extends keyof T ? Column<T, K> : never : never;
در هر صورت، نوع RowsAndColumns شما سپس از SomeColumn به جای Column استفاده میکند:
type RowsAndColumns<T extends RowData> = {
rows: Array<T>;
columns: Array<SomeColumn<T>>;
}
و این باعث میشود مورد استفاده مورد نظر شما همانطور که انتظار دارید بدون خطاهای کامپایل کار کند:
myFn({
rows: [
{
id: "foo",
bar: "bar",
bing: "bing",
bong: {
a: 99,
b: "aaa"
}
}
],
columns: [
{
key: "bar",
action: (value, rowData) => {
console.log(value);
}
},
{
key: "bong",
action: (value, rowData) => {
console.log(value.a);
}
},
]
});
لینک زمین بازی به کد
