reactjs - คำนวณประเภทของค่าตามคีย์พร็อพเพอร์ตี้ในฟังก์ชันอย่างไดนามิก
ฉันมีฮุกแบบกำหนดเองที่เพิ่มลงในฟังก์ชัน useState ซึ่งรับ props ของ key และ value
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
export type HandleModelChangeFn<T> = (key: keyof T, value: T[typeof key]) => void;
const useModel = <T>(initialModel: T): [T, HandleModelChangeFn<T>, Dispatch<SetStateAction<T>>] => {
const [model, setModel] = useState<T>(initialModel);
const handleChange = useCallback((key: keyof T, value: T[typeof key]) => {
setModel((prevState) => ({
...prevState,
[key]: value,
}));
}, []);
return [model, handleChange, setModel];
};
export default useModel;
ปัญหาคือเมื่อฉันใช้ฟังก์ชันนั้น พารามิเตอร์ตัวที่สองสามารถเป็นได้ทุกประเภทที่มีอยู่ในอินเทอร์เฟซ เช่น ตัวอย่าง
interface User {
name: string;
age: number;
}
const initialModel: User = {
name: '',
age: 1
}
const [model, handleModelChange] = useModel(initialModel);
handleModelChange('age', 'John'); // should get error cause value is wrong type
พารามิเตอร์ key สามารถเป็น 'name' หรือ 'age' แต่พารามิเตอร์ที่สองมีชนิดเป็น string | number;
ฉันต้องการคำนวณประเภทแบบไดนามิกตามคุณสมบัติของคีย์ เช่น เมื่อพารามิเตอร์คีย์เป็น 'age' TypeScript จะบอกฉันว่าฉันส่งค่าประเภทผิด เพราะประเภทของ age คือ number
มีวิธีทำสิ่งนี้ด้วย TypeScript ไหม?
คำตอบ #1
คุณต้องทำการเปลี่ยนแปลงเล็กน้อยใน HandleModelChangeFn คุณต้องอนุมาน key:
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
// added -----------------------> <Key extends keyof T>
export type HandleModelChangeFn<T> = <Key extends keyof T>(key: Key, value: T[Key]) => void;
const useModel = <T,>(initialModel: T): [T, HandleModelChangeFn<T>, Dispatch<SetStateAction<T>>] => {
const [model, setModel] = useState<T>(initialModel);
const handleChange = useCallback((key: keyof T, value: T[typeof key]) => {
setModel((prevState) => ({
...prevState,
[key]: value,
}));
}, []);
return [model, handleChange, setModel];
};
export default useModel;
interface User {
name: string;
age: number;
}
const initialModel: User = {
name: '',
age: 1
}
const [model, handleModelChange] = useModel(initialModel);
handleModelChange('age', 'John'); // error
handleModelChange('age', 2); // ok
สนามเด็กเล่น
คำตอบ #2
ฉันจะบอกว่าคุณเพียงแค่ส่งวัตถุเป็นพารามิเตอร์ไปยังฟังก์ชัน useCallback
...
// I use the partial interface so you can pass the keys individually
const handleChange = useCallback(obj: Partial<User>) => {
setModel((prevState) => Object.assign(prevState, obj));
}, []);
...
ด้วยวิธีนี้ TypeScript จะคาดหวังเฉพาะคุณสมบัติ name และ age จากวัตถุและประเภทที่จะคาดหวังเมื่อใช้คุณสมบัติใดคุณสมบัติหนึ่ง
handleModelChange({ age: 'John' }); // Error
handlModelChange({ name: 'John' }) // Success
