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

ฉันจะกำหนดได้อย่างไรว่า Typescript generic T[K] เป็นประเภทตัวเลข?


ฉันมีฟังก์ชัน Typescript ง่าย ๆ แบบนี้:

function getProperty<T, K extends keyof T>(obj: T, key: K): number {
  return obj[key]; // This line is not compiling.
  // Typescript will yell: "Type 'T[K]' is not assignable to type 'number'."
}

การใช้งานของฉันดูเหมือนนี้:

const someObj = {
  myValue: 123,
  otherProperty: '321'
}

getProperty(someObj, 'myValue')

ฉันจะไม่รู้ว่าโครงสร้างของ someObj จะเป็นอย่างไร

คำถามของฉันคือ: ฉันสามารถระบุได้อย่างไรว่า T[K] เป็นประเภทตัวเลขแบบสแตติก?

คำตอบ #1

ถ้าวัตถุที่คุณกำลังทำงานด้วยมีการกำหนดชนิดมากพอที่จะทำให้ TypeScript รู้รูปร่างของมันแล้ว คุณสามารถทำแบบนี้ได้เลย [ลิงก์ playground ของ TypeScript ถ้าคุณอยากทดลองเล็กน้อย]

คุณเพียงแค่ต้องจำกัดว่า identifier key สามารถเป็นเพียงหนึ่งในสิ่งนั้นที่อ้างอิงคุณสมบัติประเภท number:

const someObj = {
  myValue: 123,
  otherProperty: '321'
};

getProperty(someObj, 'myValue'); //       works
getProperty(someObj, 'otherProperty'); // wails

function getProperty<T, K extends NumericProps<T>>(obj: T, key: K): number {
    return obj[key];
}

type NumericProps<T> = {
    [K in keyof T]: T[K] extends number ? K : never
}[keyof T];

คำตอบ #2

ฉันจะไม่รู้ว่าโครงสร้างของ someObj จะเป็นอย่างไร

แล้ว TypeScript ก็ไม่สามารถช่วยคุณได้จริง ๆ ในเรื่องนี้ การตรวจสอบชนิดของ TypeScript จะทำที่ เวลาแปลโค้ด หากโครงสร้างของ someObj จะทราบได้ก็ต่อเมื่อ เวลารันไทม์ TypeScript จึงไม่สามารถทำให้การเข้าถึงโครงสร้างนั้นปลอดภัยตามชนิดได้ คุณจำเป็นต้องรู้ว่าคีย์ของคุณสมบัติและค่าที่เป็นไปได้ของคุณสมบัติเหล่านั้นคืออะไร ณ เวลาแปลโค้ด

ตัวอย่างเช่น: ในตัวอย่างของคุณ ชื่อสมบัติเป็นสตริงและค่าของสมบัติเป็นสตริงหรือจำนวน (แต่ไม่ใช่บูลีนหรืออ็อบเจ็กต์ เป็นต้น) คุณสามารถประกาศชนิดที่มีดัชนีตามสตริง (เนื่องจากชื่อสมบัติทั้งหมดสุดท้ายเป็นสตริงหรือสัญลักษณ์ ในกรณีนี้คือสตริง) ที่ค่าของสมบัติเป็นตัวเลขหรือสตริง:

declare type SomeObjType = {
    [key: string]: number | string
};

และจากนั้น getProperty คือ:

function getProperty<T extends SomeObjType>(obj: T, key: string): number | string {
  return obj[key];
}

และคุณสามารถใช้มันแบบนี้ (ในกรณีนี้ ผมใช้ JSON.parse เพื่อจำลองการรับข้อมูลนี้จากภายนอกขอบเขตของโปรแกรม):

const someObj: SomeObjType = JSON.parse(`{
  "myValue": 123,
  "otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));

บนสนามเด็กเล่น

แต่นั่นก็ไม่ได้ช่วยอะไรมากนัก และยังปิดกั้นความเป็นไปได้ที่ค่าของทรัพย์สินจะเป็นสิ่งอื่นนอกจากตัวเลขหรือสตริง

คุณอาจจำเป็นต้องใช้ object:

function getProperty(obj: object, key: string) {
  return obj[key];
}

const someObj = JSON.parse(`{
  "myValue": 123,
  "otherProperty": "321"
}`);

console.log(getProperty(someObj, 'myValue'));
console.log(getProperty(someObj, 'otherProperty'));

บนสนามเด็กเล่น

บทความที่เกี่ยวข้อง