ค้นหาเว็บไซต์

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