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

javascript - กำหนดคลาสในโมดูล Typescript


ฉันกำลังเขียนโมดูล Typescript สำหรับไลบรารีภายนอก

นี่คือตัวอย่างของ index.d.ts ของฉัน

declare module 'my-external-library' {
  export class MyComponent extends React.Component<MyComponentProps> {}

  export class CustomClass {
    constructor(
     firstParam: string,
     secondParam: string,
    )
  }

  interface MyComponentProps {
    config: string
    customClass: CustomClass
    text?: string
    customFn?: (input: string) => boolean
  }

}

สิ่งนี้ใช้งานได้ดี ยกเว้น customClass prop. Custom Class เป็นคลาสที่ต้องเป็นแบบนี้:

class customClass {
  constructor(firstParam, secondParam) {
    this.firstParam = firstParam
    this.secondParam = secondParam
  }

  ... all the methods you want
}

export default customClass

ในไฟล์แยกต่างหาก ฉันให้ props customClass กับ MyComponent แบบนี้:

import customClass from './customClass'

<MyComponent
    config: "test"
    customClass: customClass
/>

customClass จริง ๆ แล้วไม่ได้รับ “ประเภทตัวสร้าง” ที่ประกาศใน index.d.ts แต่คุณสมบัติอื่น ๆ ของคอมโพเนนต์ทำงานได้ดี ดังนั้น การนิยามคลาสที่ฉันสร้างมาถูกต้องหรือไม่? ฉันต้องส่งออกคลาสด้วยวิธีอื่นหรือไม่?

นี่คือ CodeSandbox: https://codesandbox.io/s/react-typescript-playground-forked-nrfk2

ขอบคุณสำหรับความช่วยเหลือใด ๆ โปรดให้อภัยความไม่มีประสบการณ์ของฉัน

คำตอบ #1

ฉันได้ทำการแยกและแก้ไข sandbox โค้ดของคุณ ลองดูสิ:

ตอนนี้ฉันจะอธิบายบางสิ่งเกี่ยวกับคลาสในไทป์สคริปต์ ซึ่งมันค่อนข้างสับสนจริงๆ

ส่วนหนึ่งเป็นเพราะว่าผู้คนใช้คำว่า "class" เพื่ออ้างถึงสิ่งที่แตกต่างกันในบริบทที่แตกต่างกัน เพื่อความกระจ่าง เรามาใช้คำที่ถูกต้องมากขึ้นกัน

ตัวสร้าง

MyPuppy มักถูกเรียกว่า "คลาส" แต่ในแง่ของ JS มันก็เป็น "ฟังก์ชันคอนสตรัคเตอร์" ด้วย ดังนั้นมาลองใช้คำว่า คอนสตรัคเตอร์ ตลอดการอภิปรายนี้กันเถอะ

// javascript
class MyPuppy {}

ตัวอย่าง

ตัวแปร puppy เป็น อินสแตนซ์ ของ MyPuppet

// javascript
const puppy = new MyPuppet()

ประเภทอินสแตนซ์ เทียบกับประเภทคอนสตรัคเตอร์

ใน TypeScript เมื่อคุณประกาศคลาสแบบด้านล่าง คุณจะ ประกาศโดยนัย ชนิดอินสแตนซ์ ที่มีชื่อเดียวกัน!

พฤติกรรมนี้ทำให้หลายคนสับสน โดยสรุป MyPuppy ในฐานะ ตัวแปร JS ถือว่าเป็น constructor ในขณะที่ MyPuppy ในฐานะ ตัวแปรประเภท TS จริงๆ แล้วถือว่าเป็น instance type

ตอนนี้เราจะอ้างอิงถึงประเภทของตัวสร้าง MyPuppy หรือที่เรียกว่า ประเภทตัวสร้าง อย่างไร? คุณควรใช้จริงๆ ว่า typeof MyPuppy.

// typescript
class MyPuppet {}

type MyPuppetInstance = MyPuppet
type MyPuppetConstructor = typeof MyPuppet

เราได้ทำการชี้แจงความหมายของคำแล้ว มาทำกรณีศึกษากันเถอะ

มีแนวคิดเกี่ยวกับ "ด้านอินสแตนซ์" และ "ด้านสแตติก" ของคลาส ซึ่งเคยถูกบันทึกไว้ในคู่มือ TS เก่า ๆ โดยพื้นฐานแล้ว "ด้านอินสแตนซ์" จะตรงกับ ประเภทอินสแตนซ์ และ "ด้านสแตติก" จะตรงกับ ประเภทคอนสตรักเตอร์

class MyPuppy {
  static getSpecies(dog: MyPuppy) {
    return dog.species
  }

  constructor(species: string) { this.species = species }

  species: string

  // [side note] above two lines can be combined as one:
  // constructor(public species: string) {}

  poop() { return '💩' }
}

type MyPuppetInstance = MyPuppet
type MyPuppetConstructor = typeof MyPuppet


var bar: MyPuppetConstructor = MyPuppy
var foo: MyPuppetInstance = new bar("shiba_inu")

โค้ดตัวอย่างด้านบนทำหลายอย่าง

  1. ประกาศ constructor ของ JS ที่ชื่อ MyPuppy
  2. ประกาศชนิด instance ของ TS MyPuppy โดยนัย
  3. ในด้าน "instance" , foo เป็น instance และมีคุณสมบัติ species และวิธี poop.
  4. ในด้าน "คงที่" bar เป็น ตัวสร้าง ซึ่งเป็น "ฟังก์ชันที่สามารถสร้างใหม่ได้" ซึ่งสามารถเรียกใช้งานได้เป็น new bar("shiba_inu") และยังมีเมธอดคงที่ getSpecies อีกด้วย

ตอนนี้ในไฟล์ .d.ts เราจะประกาศประเภทข้างต้นอย่างไร? คุณสามารถใช้ได้ทั้งคีย์เวิร์ด interface หรือคีย์เวิร์ด type พวกมันเกือบจะไม่ต่างกันเลย

// index.d.ts

interface MyPuppyInstance {
  species: string
  poop(): string
}

interface MyPuppyConstructor {
  new (species: string): MyPuppyInstance
  getSpecies(dog: { species: string }): string
}

type MyPuppyInstance2 = {
  species: string
  poop(): string
}

type MyPuppyConstructor2 = 
  (new (species: string) => MyPuppyInstance)
  & { getSpecies(dog: { species: string }): string }