# Cách để khai báo 1 prop không được chấp nhận hoặc cảnh báo không tồn tại Full (ok)

### Trước

components\Test.tsx

```typescript
import React from 'react';
export default function components() {
  return (
    <div>
      <CustomInput fieldName="colorField" />
    </div>
  )
}
```

<figure><img src="https://2086325979-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOfwU52oyTCInVkN26DVZ%2Fuploads%2F4jh43mfOuOguTfLI4Tj2%2Fimage.png?alt=media&#x26;token=538c3391-560a-42ae-8621-d8d70a1231eb" alt=""><figcaption></figcaption></figure>

### Sau

components\Test.tsx

```typescript
import React from 'react';
interface CustomInputProps {
  fieldName: string;
}
class CustomInput extends React.Component<CustomInputProps> {
  render() {
    return <input name={this.props.fieldName} />
  }
}
export default function components() {
  return (
    <div>
      <CustomInput fieldName="colorField" />
    </div>
  )
}
```

<figure><img src="https://2086325979-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOfwU52oyTCInVkN26DVZ%2Fuploads%2FI7IOnZMv5Imo4dKb61jB%2Fimage.png?alt=media&#x26;token=33666cd5-d9b6-4173-84a4-f7caca8b7a9c" alt=""><figcaption></figcaption></figure>

## Giải mã lỗi React của TypeScript <a href="#id-7314" id="id-7314"></a>

[![Fiona Hopkins](https://miro.medium.com/v2/resize:fill:44:44/1*J5xY58uqhfmHfOlTThLDfg.jpeg)](https://medium.com/@fionawhim?source=post_page---byline--8704cc9ef402--------------------------------)[![Đổi mới và Công nghệ](https://miro.medium.com/v2/resize:fill:24:24/1*_JLNKx2yqO48v9JV-Py0Ng.png)](https://medium.com/innovation-and-technology?source=post_page---byline--8704cc9ef402--------------------------------)

[Fiona Hopkins](https://medium.com/@fionawhim?source=post_page---byline--8704cc9ef402--------------------------------)

·

Theo

Đã xuất bản trong[Đổi mới và Công nghệ](https://medium.com/innovation-and-technology?source=post_page---byline--8704cc9ef402--------------------------------)·16 phút đọc·Ngày 28 tháng 6 năm 2018

2,2 nghìn

11

Kiểm tra kiểu tĩnh là một trong những biện pháp bảo vệ kỹ thuật yêu thích của tôi, đó là lý do tại sao tôi ưu tiên áp dụng trình kiểm tra kiểu khi [chọn bộ công cụ](https://medium.com/innovation-and-technology/bringing-death-certificates-to-life-with-agile-tools-dcb0174cdc1a) phát triển ứng dụng web trong [nhóm Kỹ thuật số của Thành phố Boston](https://www.boston.gov/digital) .

Kiểm tra kiểu tĩnh giảm rủi ro bảo trì phần mềm, vì nó chỉ ra những nơi bạn cần cập nhật mã để phù hợp với những thay đổi bạn đang thực hiện. Nó cũng tốt cho phát triển mới vì, với sự hỗ trợ của trình soạn thảo tốt, nó đảm bảo bạn đang gọi các hàm hiện có và cung cấp cho chúng các đối số mà chúng mong đợi.

Nhưng thứ sáu tuần trước, tôi nhận được tin nhắn trực tiếp từ đồng nghiệp John, người đã viết một số thành phần React:

> Tôi ghét những lỗi đó, tôi định nói là không thích nhưng naaaaahhh tôi ghét chúng lol

John là một sinh viên mới tốt nghiệp trại huấn luyện, làm việc với tôi vào mùa hè với tư cách là một thành viên. Anh ấy cũng rất thoải mái, vì vậy việc nhìn thấy phản ứng mạnh mẽ này thực sự đánh thức tôi.

*Tôi đã ném John vào thế giới kiểm tra kiểu mà không có sự chuẩn bị.* Anh ấy đã trở thành nạn nhân của một **nghịch lý về rào chắn** : việc xử lý các thông báo lỗi "hữu ích" của trình kiểm tra có thể tốn nhiều công sức hơn là gỡ lỗi các vấn đề mà trình kiểm tra cảnh báo bạn.

Sự kết hợp của React, JSX và DOM tạo ra một số loại TypeScript phức tạp. Khi TypeScript phàn nàn, thông báo lỗi của nó rất dài dòng, với rất nhiều tên mà bạn không nhận ra từ mã của riêng bạn. Vì vậy, đối với John và tất cả những người mới làm quen với môi trường này, tôi xin giới thiệu hướng dẫn của mình để giải mã thông báo lỗi React / TypeScript của bạn.

(Bạn có thể nhận thấy rằng ứng dụng web Registry được viết bằng [Flow](https://flow.org/) . Đối với phần phát triển mới, chúng tôi đang làm việc bằng [TypeScript](https://www.typescriptlang.org/) .)

## Tại sao lại phải kiểm tra kiểu React? <a href="#id-27c1" id="id-27c1"></a>

Hãy cùng xem chúng ta hy vọng đạt được điều gì khi sử dụng TypeScript với React ngay từ đầu. Khi mọi thứ diễn ra suôn sẻ, chúng ta sẽ bắt được những lỗi này:

* Cố gắng truyền một prop mà một thành phần không muốn
* Quên truyền một prop mà một thành phần yêu cầu
* Nhận sai loại của một prop, chẳng hạn như truyền một chuỗi khi nó mong đợi một số

Nếu chúng ta viết những lỗi này, TypeScript sẽ hiển thị lỗi ngay trong trình soạn thảo của chúng ta. Nếu không có TypeScript, chúng ta sẽ phải phát hiện những lỗi này sau trong quá trình thử nghiệm và có thể sẽ rất tẻ nhạt khi phải gỡ lỗi để tìm ra lỗi xuất phát từ đâu.

(Có đáng để sử dụng công cụ để phát hiện những lỗi này hay không là câu hỏi dành cho nhóm của bạn. Tôi thấy nó rất hữu ích, đó là lý do chúng tôi ở đây.)

## Lỗi với các phần tử DOM <a href="#fbaa" id="fbaa"></a>

Để bắt đầu, hãy xem xét việc sử dụng các phần tử DOM chuẩn trong JSX. TypeScript sẽ kiểm tra để đảm bảo rằng mọi thuộc tính bạn đặt vào thẻ HTML đều tồn tại và có đúng loại. Ví dụ:

```
render() { 
  trả về <kiểu đầu vào="văn bản" tên="màu" />; 
}
```

Kiểu mã React/JSX ở trên kiểm tra tốt, vì `<input>`s có cả thuộc tính `type`và `name`! Bạn sẽ không thấy bất kỳ lỗi nào. TypeScript có thể làm điều này vì có một thư viện React ( `@types/react`) định nghĩa tất cả các phần tử HTML và các thuộc tính mà mỗi phần tử có.

**Lưu ý:** Trong hướng dẫn này, tôi sử dụng "thuộc tính" và "thuộc tính" thay thế cho nhau. "Thuộc tính" là tên gọi của chúng trong HTML, nhưng trên các thành phần React, chúng là "props". Và khi chúng nằm trên Đối tượng JavaScript, TypeScript gọi chúng là "thuộc tính".

### Sử dụng một thuộc tính không tồn tại <a href="#id-325c" id="id-325c"></a>

Bây giờ chúng ta hãy viết sai chữ “ `name`” và xem điều gì xảy ra:

<pre><code>render() { 
<strong>  trả về &#x3C;kiểu đầu vào="văn bản" nmae ="màu" />; 
</strong>}
</code></pre>

Nếu trình soạn thảo của bạn hỗ trợ TypeScript, bạn sẽ thấy dòng đó có gạch chân màu đỏ và thông báo lỗi sau:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*XDmLfWXuRKhTkjFjV9HjuQ.png" alt="" height="144" width="700"><figcaption><p>“Thuộc tính 'nmae' không tồn tại trên loại 'DetailedHTMLProps&#x3C;InputHTMLAttributes&#x3C;HTMLInputElement>, HTMLInputElement>'”</p></figcaption></figure>

Có vẻ hơi khó khăn, nhưng chúng ta có thể phân tích từng phần một:

* `Property 'nmae'`— “Thuộc tính” là các thuộc tính trên `<input>`thẻ. Vì vậy, lỗi này liên quan đến “ `nmae=”…"`”.
* `does not exist on type`— Có một “kiểu”, một định nghĩa ở đâu đó về những gì được phép có trên `<input>`, và “ `nmae`” không có trong danh sách.
* `'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'`— OK, đây là một điều khó khăn. Điều này mô tả cách TypeScript nghĩ về `<input>`các thuộc tính của ’. Tất cả các tên này ( `DetailedHTMLProps`, `InputHTMLAttributes`, và `HTMLInputElement`) được định nghĩa trong thư viện React đó. `DetailedHTMLProps`có nghĩa là các thuộc tính mà bất kỳ phần tử HTML nào cũng có thể có (như `id`, `tabIndex`, và `style`) và `InputHTMLAttributes`dành cho các thuộc tính cụ thể cho `<input>`các phần tử ( `name`, `value`, *\&c.* ). Đây là bản mẫu mà bạn thường có thể bỏ qua, nhưng `HTML***Element`phần này có thể là một manh mối hữu ích để giải mã điều này.

**Bản sửa lỗi:** Khi bạn thấy “ `Property XXX does not exist on type`,” nghĩa là bạn đang thêm an `XXX="…"`vào một phần tử không muốn có an `XXX`trên đó. Mặc dù bản thân điều đó không phải là vấn đề—phần tử sẽ chỉ bỏ qua các thuộc tính mà nó không nhận ra—nhưng thường thì đó là dấu hiệu của lỗi. Trong trường hợp này, việc thêm a không phải là vấn đề `nmae`, nhưng đó là vấn đề mà chúng ta *nghĩ rằng* mình đã thêm a `name`nhưng thực tế lại không phải vậy.

Khi bạn thấy điều này, hãy kiểm tra lỗi đánh máy và kiểm tra tài liệu HTML để đảm bảo bạn có thuộc tính hoặc phần tử phù hợp với mục đích bạn đang cố gắng thực hiện.

### Nhận sai loại thuộc tính <a href="#id-7fe4" id="id-7fe4"></a>

Có một lỗi khác có thể xảy ra với HTML: sử dụng sai kiểu cho một thuộc tính. Hãy thêm a `size`vào `<input>`và xem điều gì xảy ra:

<pre><code>render() { 
<strong>  trả về &#x3C;kiểu đầu vào="văn bản" tên="màu" kích thước="6" />; 
</strong>}
</code></pre>

Đây chính xác là cách bạn viết trong trang HTML, nhưng TypeScript lại hiển thị lỗi:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*mEy2LH0UmjN17kSsm64chw.png" alt="" height="145" width="700"><figcaption><p>Các loại thuộc tính 'size' không tương thích. Loại 'string' không thể gán cho loại 'number | undefined'.</p></figcaption></figure>

* `Types of property 'size' are incompatible.`— Chúng ta đang nói về `size`hiện tại. Lưu ý cách nó nói "types" ở dạng số nhiều. Đó là vì *tất cả các lỗi kiểu đều là **sự không nhất quán** .* Lỗi này nằm giữa hai kiểu: một kiểu từ mã của chúng ta và một kiểu từ `<input>`định nghĩa của React (hãy nhớ lại `DetailedHTMLProps<…>`phần chúng ta đã thấy trong lỗi trước).
* `Type 'string'`— Đây là kiểu đầu tiên, kiểu cho đối số mà chúng ta đã viết, `"6"` *là* một chuỗi, vì *tất cả* các thuộc tính JSX được trích dẫn đều là chuỗi.
* `is not assignable to`— Đây là cách diễn đạt tinh tế của từ “không phải”. Nó làm rõ điều “không tương thích” giữa hai loại.
* `type 'number | undefined'`— Đây là kiểu thứ hai trong hai kiểu, kiểu mà thư viện React cho là phù hợp với thuộc tính `<input>`'s `size`. The `|`được gọi là toán tử “union”, nhưng bạn có thể đọc là “or”: “ `size`phải là một số hoặc không xác định”.

**Sửa lỗi:** Chúng ta cần thay đổi một trong hai kiểu để làm cho chúng nhất quán. Vì chúng ta không thể thay đổi thư viện React để chấp nhận một chuỗi, chúng ta phải thay đổi mã của mình để truyền một số:

```
render() { 
  <input type="text" name="color" size={6} /> 
}
```

Hãy nhớ rằng đó `{}`là cách tạo biểu thức JavaScript trong JSX. Bây giờ thay vì chuỗi, chúng ta cung cấp số và lỗi TypeScript sẽ biến mất.

Thông báo lỗi cho biết kiểu từ thư viện React là `'number | undefined'`. “Undefined” giống như việc bỏ nó đi, đó là lý do tại sao các ví dụ trước của chúng tôi đã kiểm tra kiểu mà không có `size`. Nhưng điều này giải thích tại sao thông báo lỗi cho biết “is not assigned to” thay vì “isn't.” `6`là một số, không phải là “number or undefined.” Nhưng, một số có *thể gán* cho “number or undefined,” vì “or.”

Thư viện kiểu React không yêu cầu bất kỳ thuộc tính nào trên các thành phần HTML, do đó không có lỗi "quên truyền prop mà một thành phần yêu cầu" trong phần này. Lỗi này sẽ xuất hiện khi chúng ta xử lý các thành phần tùy chỉnh.

## Tất cả các lỗi kiểu đều là sự không nhất quán <a href="#id-97e6" id="id-97e6"></a>

Tôi đã đề cập đến điều này ở trên trong phần phân tích `size`lỗi, nhưng đây là một khái niệm quan trọng đến mức tôi muốn nhắc lại:

> *Tất cả các lỗi kiểu đều là **sự không nhất quán** .*

Ý tôi là những dòng chữ TypeScript màu đỏ đó ở đó vì một phần trong mã của bạn không phù hợp với phần khác. Phần nào đúng? Công việc của bạn là tìm ra điều đó bằng cách suy nghĩ về những gì bạn đang cố gắng làm cho mã của mình thực hiện.

Một trong những lý do khiến lỗi kiểu có thể gây nhầm lẫn khi diễn giải là TypeScript sẽ chỉ gạch chân *một* trong những nơi xảy ra sự không nhất quán. Ví dụ đầu tiên bên dưới là lỗi khi TypeScript gọi ra một cái gì đó trong JSX, nhưng phần đó thực sự đúng và chúng ta quyết định định nghĩa kiểu là sai.

## Lỗi với các thành phần React tùy chỉnh <a href="#id-4ae0" id="id-4ae0"></a>

Nguyên tắc kiểm tra kiểu của các thành phần tùy chỉnh về cơ bản giống với các phần tử DOM, nhưng tên của các kiểu lại khó hiểu hơn.

Đối với phần này, chúng tôi sẽ sử dụng thành phần mẫu sau:

```
lớp CustomInput mở rộng React.Component { 
  render() { 
    return <input />; 
  } 
}
```

### Sử dụng đạo cụ của riêng bạn <a href="#d061" id="d061"></a>

Giả sử chúng ta sử dụng this để xây dựng một form, vì vậy chúng ta cần truyền vào một `name`phương thức có thể đi vào đó `<input>`. Trong React, điều đó có nghĩa là thêm một prop. Đây là `render`phương thức mới:

<pre><code>render() { 
<strong>  trả về &#x3C; tên đầu vào={this.props.fieldName} /> 
</strong>}
</code></pre>

Có thể được sử dụng bởi một thành phần khác như thế này:

```
render() { 
  trả về ( 
    <form> 
      <CustomInput fieldName="colorField" /> 
    </form> 
  ); 
}
```

Có vẻ đơn giản, và trong JavaScript thông thường trường hợp này hoàn toàn có thể hoạt động, không có lỗi. Nhưng TypeScript đã tìm thấy điều gì đó để phàn nàn:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*a3gaehQXBm739VbNZ1sDQw.png" alt="" height="162" width="700"><figcaption><p>Thuộc tính 'fieldName' không tồn tại ở kiểu 'Readonly&#x3C;{ children?: ReactNode; }> &#x26; Readonly&#x3C;{}>'.</p></figcaption></figure>

* `Property 'fieldName'`— Chúng ta đã thấy ngôn ngữ này trước đây với thuộc tính `<input>`'s `nmae`, nhưng bây giờ nó đề cập đến một “thuộc tính” trên `this.props`đối tượng, thay vì một thuộc tính trên thẻ HTML.
* `does not exist on type`— Đây cũng là cùng một ngôn ngữ. TypeScript có ý kiến ​​về những thuộc tính nào là và không phải là trên `this.props`, và `fieldName`nằm trong đống “không”.
* `'Readonly<{ children?: ReactNode; }> & Readonly<{}>'`— Được rồi, điều này khó đọc. Đây là kiểu mà TypeScript nghĩ đến khi chúng ta nói `this.props`.

Phần cuối cùng cần được giải thích chi tiết hơn:

* s `Readonly`có nghĩa là bạn không thể thay đổi `this.props`. Bạn không nên làm điều đó với React, vì vậy TypeScript thực thi điều đó.
* `{ children?: ReactNode; }`có ở đó không vì tất cả các thành phần React *đều có thể* có `this.props.children`giá trị. `ReactNode`là bất kỳ thứ gì mà React biết cách hiển thị: chuỗi, thành phần, v.v. Điều đó `?`có nghĩa là thuộc tính này là tùy chọn—bạn không cần phải truyền các thành phần con cho `CustomInput`.
* `&`trong TypeScript tạo ra một "giao điểm" giữa hai kiểu, giống như một đối tượng có tất cả các thuộc tính từ mỗi kiểu được hợp nhất với nhau. Vì vậy, `this.props`có một kiểu được tạo thành từ tất cả các thuộc tính của `{ children?: ReactNode; }`kết hợp với tất cả các thuộc tính của `{}`. Mà không có bất kỳ thuộc tính nào.

Điều này có nghĩa là `this.props`có một `children`thuộc tính tùy chọn nhưng không có `fieldName`thuộc tính nào.

**Cách khắc phục:** Để khắc phục điều này, chúng ta thực sự phải đưa ra lựa chọn. Hãy nói cùng tôi ngay bây giờ:

> *Tất cả các lỗi kiểu đều là **sự không nhất quán** .*

Có hai điều không đồng ý. Trong `render`phương pháp của bạn, bạn đang cố gắng sử dụng `this.props.fieldName`. Nhưng khi bạn khai báo thành phần của mình, bạn đã không cung cấp cho nó một `fieldName`prop. Cái nào đúng? `render`Phương pháp hay khai báo thành phần?

Với các phần tử DOM, chúng ta biết rằng các thuộc tính mong đợi là chính xác vì chúng đến từ một thư viện mà chúng ta tin tưởng. Nhưng đây là mã của riêng chúng ta, vì vậy chúng ta phải chọn những gì cần thay đổi để lỗi biến mất. Điều đó có nghĩa là ngừng cố gắng sử dụng `this.props.fieldName`, hoặc thêm `fieldName`vào khai báo thành phần của chúng ta.

Theo mặc định, tất cả `React.Component`s đều có `this.props`kiểu `{}`, mà chúng ta thấy trong `&`câu lệnh trong lỗi. Vì chúng ta thực sự muốn có một `fieldName`prop, chúng ta phải thêm nó:

<pre><code><strong>giao diện CustomInputProps { 
</strong><strong>  fieldName: string; 
</strong><strong>}lớp CustomInput mở rộng React.Component &#x3C;CustomInputProps> { 
</strong>  render() { 
    trả về &#x3C;input name={this.props.fieldName} />; 
  } 
}
</code></pre>

Chuyện gì đang xảy ra ở đây? Chúng ta bắt đầu với `interface`, đó là cách chúng ta tạo một "kiểu đối tượng" mới trong TypeScript. Chúng ta đang nói rằng bất kỳ đối tượng nào có kiểu `CustomInputProps`phải là "một đối tượng có thuộc `fieldName`tính, phải là một chuỗi".

### Ngoài ra: Tại sao chúng ta lại nói về các loại đối tượng? <a href="#fc6b" id="fc6b"></a>

`CustomInputProps`là một kiểu đối tượng, có nghĩa là nó mô tả “hình dạng” của một đối tượng JavaScript. Ví dụ:

```
const obj: CustomInputProps = { 
  fieldName: 'myInput', 
};
```

Đó là một `const`lệnh được gọi `obj`và `: CustomInputProps`cho TypeScript biết rằng giá trị của nó cần phải khớp với `CustomInputProps`hình dạng. Chúng tôi khởi tạo nó bằng một đối tượng theo nghĩa đen có `fieldName`thuộc tính cần thiết, do đó nó kiểm tra kiểu!

Chúng ta hãy bỏ qua `fieldName`phần bất động sản và xem điều gì xảy ra:

<pre><code><strong>const obj: CustomInputProps = {} ;
</strong></code></pre>

`{}`không khớp `CustomInputProps`, vì vậy đây là cách TypeScript phàn nàn:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*L04lT463InLLj3gWGt1sWA.png" alt="" height="193" width="700"><figcaption><p>Kiểu '{}' không thể gán cho kiểu 'CustomInputProps'. Thuộc tính 'fieldName' bị thiếu trong kiểu '{}'.</p></figcaption></figure>

* `Type '{}'`— Đây là TypeScript mô tả kiểu giá trị mà chúng ta đang cố gắng gán cho `obj`: một đối tượng không có trường nào.
* `is not assignable to`— Người bạn cũ của chúng ta “không thể gán cho”. Chúng ta đang cố gắng thực hiện một phép gán: gán cho `{}`, `obj`nhưng TypeScript lại nói rằng chúng ta không thể làm điều đó.
* `type 'CustomInputProps'.`— Đây là vì nó là thứ chúng ta đã tuyên bố `obj`. Do đó, nó là kiểu được gán *cho* .
* `Property 'fieldName' is missing in type '{}'.`— Bây giờ TypeScript đang nêu rõ lý do tại sao một đối tượng có kiểu `{}`không thể gán cho thứ gì đó có kiểu `CustomInputProps`: Không có `fieldName`, và, theo `interface`khai báo ở trên, tất cả `CustomInputProps`các đối tượng phải có `fieldName`.

Bây giờ chúng ta hãy xem điều gì xảy ra khi chúng ta đưa trở lại `fieldName`, nhưng cũng thêm một trường không phải là một phần của `CustomInputProps`kiểu:

<pre><code>const obj: CustomInputProps = { 
  fieldName: 'myInput', 
<strong>  placeholder: 'Nhập nội dung gì đó vào đây',
</strong> };
</code></pre>

Điều đó không được phép:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*4yxfnrzn640KhdGX33YxWw.png" alt="" height="236" width="700"><figcaption><p>Kiểu '{ fieldName: string; placeholder: string; }' không thể gán cho kiểu 'CustomInputProps'. Đối tượng theo nghĩa đen chỉ có thể chỉ định các thuộc tính đã biết và 'placeholder' không tồn tại trong kiểu 'CustomInputProps'.</p></figcaption></figure>

* `Type '{ fieldName: string; placeholder: string; }'`— Đây là kiểu mới của đối tượng theo nghĩa đen của chúng ta. Hãy xem cách nó có `placeholder`thuộc tính này.
* `is not assignable to type 'CustomInputProps'.`— Giống như trước. TypeScript cho rằng lệnh gán này là bất hợp pháp.
* `Object literal may only specify known properties, and 'placeholder' does not exist in type 'CustomInputProps'.`— Đây là lời giải thích cho lý do tại sao trường hợp này “không thể gán được.” `CustomInputProps`không có `placeholder`thuộc tính được khai báo trong `interface`khối của nó.

Đây là trường hợp tương tự như khi chúng ta thêm `nmae`. `<input>`Việc thêm thuộc tính bổ sung `placeholder`không phải là lỗi, vì mã đang chạy `CustomInputProps`sẽ bỏ qua nó, nhưng đó là *dấu hiệu* cho thấy có thể có lỗi.

Quay lại lý do tại sao chúng ta lại nói về các kiểu đối tượng ngay từ đầu. Chúng liên quan gì đến React props? Đối tượng có `{}`dấu ngoặc nhọn ngoằn ngoèo, không phải `<>`dấu ngoặc nhọn của JSX. Để giải thích điều này, hãy quay lại ví dụ về phần tử DOM của chúng ta và nói một chút về JSX.

```
<input name="color" size={6} />
```

Trình duyệt không thể tự hiểu JSX này, chúng chỉ hiểu JavaScript thuần túy. Chúng tôi thường sử dụng trình biên dịch TypeScript hoặc một công cụ như [Babel](https://babeljs.io/) để lấy JSX và tạo JavaScript từ đó. Sau đây là đoạn mã trông như thế nào dưới dạng JavaScript:

```
React.createElement('đầu vào', { 
  tên: 'màu', 
  kích thước: 6, 
});
```

Này, những thứ đó `name="color"`và `size={6}`các thuộc tính bây giờ là một đối tượng theo nghĩa đen! Đối số đầu tiên `createElement`là những gì chúng ta đang tạo ra—một lớp cho các thành phần tùy chỉnh, một tên thẻ cho các phần tử HTML—và đối số thứ hai là các props để truyền vào nó.

Đây là lý do tại sao TypeScript luôn nói về props như các kiểu đối tượng. Khi kiểm tra kiểu mã React, nó đảm bảo rằng đối số thứ hai `createElement`có kiểu khớp với (“có thể gán cho”) kiểu đã khai báo của props của thành phần.

(Vì nó chỉ chuyển đổi mọi thứ thành JavaScript thuần túy, nên JSX đôi khi được gọi là "cú pháp ngọt ngào". Thực ra, nó không cần thiết—chúng ta có thể `createElement`tự viết những câu lệnh đó—nhưng nó làm cho mã trở nên ngọt ngào hơn.)

### Bây giờ quay lại khai báo kiểu props <a href="#d888" id="d888"></a>

Hãy cùng nhớ lại chúng ta đã ở đâu:

<pre><code><strong>giao diện CustomInputProps { 
</strong><strong>  fieldName: string; 
</strong><strong>}lớp CustomInput mở rộng React.Component &#x3C;CustomInputProps> { 
</strong>  render() { 
    trả về &#x3C;input name={this.props.fieldName} />; 
  } 
}
</code></pre>

Bây giờ chúng ta đã khai báo the `interface`we want—“hình dạng” của props của thành phần—chúng ta thực sự cần phải bảo TypeScript sử dụng nó cho `CustomInput`. Đó là nơi the `React.Component<CustomInputProps>`xuất hiện. Các `<…>`bit cho phép chúng ta truyền *tham số kiểu* . Hãy nghĩ về nó như một cách để điều chỉnh `React.Component`, giống như cách bạn sử dụng `(…)`s để truyền đối số cho các hàm. `ReactComponent`sử dụng tham số kiểu đầu tiên của nó làm kiểu for `this.props`, thay thế mặc định `{}`.

Chúng ta có thể quay lại `render`phương pháp của mình và xem `this.props`hiện tại là gì. Trong Visual Studio Code, bạn có thể di chuột qua nó:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*bUAo19cXzh4ycUKvLW93xQ.png" alt="" height="162" width="700"><figcaption><p>React.Component&#x3C;CustomInputProps, {}, bất kỳ>.props: Chỉ đọc&#x3C;{ children?: React.ReactNode; }> &#x26; Chỉ đọc&#x3C;CustomInputProps></p></figcaption></figure>

* `React.Component<CustomInputProps, {}, any>`— Đây là Visual Studio cho chúng ta biết điều gì `this`. Nó gần giống với `extends React.Component<CustomInputProps>`khai báo lớp của chúng ta, chỉ với các giá trị mặc định cho `CustomInputProps`2 tham số kiểu bổ sung được viết rõ ràng.
* `.props:`— Bởi vì đó chính là thứ chúng ta đang quan tâm.
* `Readonly<{ children?: React.ReactNode; }> & Readonly<CustomInputProps>`— Bạn còn nhớ `Readonly`thứ này từ trước không? Bây giờ thì nó `CustomInputProps`thay thế cho `{}`.

Tuyệt! Với thay đổi này, `this.props`vẫn giữ nguyên thuộc tính trước đó `children`nhưng giờ cũng có mọi thứ từ `CustomInputProps`, đặc biệt là `fieldName`thuộc tính.

### Ngoài ra còn có trạng thái <a href="#id-77cf" id="id-77cf"></a>

Sử dụng `this.state`gần giống với `this.props`, vì vậy tôi sẽ không đi sâu vào nó. Các lỗi bạn nhận được từ nó về cơ bản sẽ giống nhau. Để cung cấp cho trạng thái của thành phần một loại, hãy sử dụng tham số loại thứ hai cho `React.Component`, như thế này:

<pre><code>giao diện Props { 
  fieldName: string; 
<strong>}giao diện State { 
</strong><strong>  lỗi: chuỗi | null; 
</strong><strong>}lớp CustomInput mở rộng React.Component&#x3C;Props, State > { 
</strong><strong>  state = { 
</strong><strong>    lỗi: null; 
</strong><strong>  };
</strong> }
</code></pre>

Ví dụ trên cũng cho thấy việc khởi tạo `state`với giá trị mặc định. Bạn cũng có thể thực hiện việc này `this.state = {…}`trong `constructor()`phương thức. Tôi cũng đã rút ngắn tên giao diện thành “ `Props`” và “ `State`” để chúng không bị bao bọc. Chúng thực sự có thể là bất kỳ thứ gì, miễn là bạn nhất quán giữa `interface`và `React.Component<…>`.

### Các props cần thiết trên một thành phần tùy chỉnh <a href="#id-004e" id="id-004e"></a>

Hãy quay lại sử dụng `CustomInput`thành phần của chúng ta. Điều này sẽ tương tự như khi chúng ta tạo `<input>`các phần tử.

Hãy nhớ lại chỗ chúng ta dừng lại `CustomInput`:

```
giao diện CustomInputProps { 
  fieldName: string; 
}lớp CustomInput mở rộng React.Component<CustomInputProps> { 
  … 
}
```

Chúng ta hãy cùng làm một cái nhé!

```
render() { 
  trả về ( 
    <form> 
      <CustomInput /> 
    </form> 
  ); 
}
```

Và lỗi là…

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*n9ulcUhM2uSzo4-7yq2ZJg.png" alt="" height="346" width="700"><figcaption><p>Kiểu '{}' không thể gán cho kiểu 'IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…'. Kiểu '{}' không thể gán cho kiểu 'Readonly&#x3C;CustomInputProps>'. Thuộc tính 'fieldName' bị thiếu trong kiểu '{}'.</p></figcaption></figure>

Mọi chuyện ngày càng trở nên phức tạp hơn, phải không?

* `Type '{}'`— Để giải thích điều này, hãy nhớ cách JSX hoạt động. Dòng mã trên của chúng ta chuyển thành `React.createElement(CustomInput, {})`. `{}`vì chúng ta không viết bất kỳ props nào trong JSX. `Type '{}'`TypeScript đang mô tả đối số props thứ hai đó.
* `is not assignable to`— Vẫn là cách cũ. Có sự không nhất quán trong các loại!
* `'IntrinsicAttributes & IntrinsicClassAttributes<CustomInput> & Readonly<{ children?: ReactNode; }>...'`— Đây là một mớ hỗn độn đến mức TypeScript thực sự từ bỏ và dừng lại trước khi đến cuối. Các bit "nội tại" bao gồm các đạo cụ React phổ biến như `key`và `ref`. Bạn có thể thấy `Readonly<{ children?: ReactNode; }>`ở cuối, đó là phần bắt đầu của `this.props`kiểu mà chúng ta đã thấy trước đó. Kiểu hoàn chỉnh ở đây là: `IntrinsicAttributes & IntrinsicClassAttributes<CustomInput> & Readonly<{ children?: ReactNode; }> & Readonly<CustomInputProps>`, nhưng thành thật mà nói, bạn có thể bỏ qua dòng này.
* `Type '{}' is not assignable to type 'Readonly<CustomInputProps>'`— Ở đây TypeScript gọi ra phần nào của `IntrinsicAttributes & … & …`nó đang gặp sự cố. `{}`có thể gán hoàn hảo cho `IntrinsicAttributes`, `IntrinsicClassAttributes`, và `Readonly<{ children?: ReactNode; }>`vì tất cả các trường của chúng đều là tùy chọn. Phần không thể gán được là `CustomInputProps`.
* `Property 'fieldName' is missing in type '{}'.`— Đây là phần hữu ích nhất của thông báo lỗi. Nó `type '{}'`gọi lại `createElement`đối số thứ hai, loại props từ JSX.

**Sửa lỗi:** Một lần nữa, lỗi kiểu là đánh dấu *sự không nhất quán* trong mã. Như đã viết, `CustomInput`muốn có một `fieldName`prop, nhưng bạn không cung cấp cho nó. Vì vậy, hãy cung cấp cho nó một prop, hoặc làm cho nó không muốn có prop ngay từ đầu. Hãy xem cách chúng ta sẽ thực hiện từng cái.

Để vượt qua `fieldName`, chúng ta làm như sau:

<pre><code><strong>&#x3C;CustomInput fieldName="myField" />
</strong></code></pre>

Sau khi dịch JSX, nó trở thành:

<pre><code><strong>React.createElement(CustomInput, { fieldName: "myField" } );
</strong></code></pre>

Kiểu của đối số props thứ hai là `{ fieldName: string }`có *thể* gán cho `CustomInputProps`.

Đây là trường hợp khác, làm cho `fieldName`tùy chọn. Đối với trường hợp này, chúng ta cần thay đổi where `interface`đã `CustomInputProps`được khai báo.

<pre><code>giao diện CustomInputProps { 
<strong>  fieldName? : string; 
</strong>}
</code></pre>

Bạn thấy đấy, có một `?`there ở đây không? Điều đó cho TypeScript biết rằng thuộc tính này là tùy chọn. Bây giờ cả hai `{ fieldName: string }`và `{}`đều có thể gán cho `CustomInputProps`.

Điều này có nghĩa là `this.props.fieldName`bây giờ có kiểu `'string | undefined'`, vì vậy TypeScript sẽ khiến bạn tính đến điều đó trong phần còn lại của mã. Không gọi `this.props.fieldName.length`mà không kiểm tra xem nó đã được định nghĩa trước chưa!

### Truyền đạo cụ không xác định <a href="#id-226d" id="id-226d"></a>

Cuối cùng, hãy xem các thông báo lỗi bạn nhận được khi bạn cung cấp một prop cho một thành phần tùy chỉnh không nằm trong loại props của nó. TypeScript có hai thông báo lỗi khác nhau, tùy thuộc vào các props khác mà bạn truyền.

```
<CustomInput fieldName="myField" placeholder="Nhập ở đây" />
```

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*4Lrl6y6lXyXmUWjenXCOLg.png" alt="" height="166" width="700"><figcaption><p>Thuộc tính 'placeholder' không tồn tại trên loại 'IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…'.</p></figcaption></figure>

Thông báo lỗi này giống với thông báo chúng ta nhận được `nmae`trong ví dụ đầu, chỉ có `CustomInput`kiểu props dài hơn ' ở cuối. Thật **không may** là thông báo này bị cắt trước khi nó nói `CustomInputProps`, vì đó là một phần thông tin *thực sự* giúp theo dõi những gì đang diễn ra. Chỉ cần nhớ rằng khi bạn thấy thông báo này, rất có thể prop bạn đang truyền không được định nghĩa trong giao diện props của chính thành phần.

Thật kỳ lạ, nếu chúng ta bỏ đi `fieldName`thì thông báo lỗi sẽ khác:

```
giao diện xuất khẩu CustomInputProps { 
  fieldName?: string; 
}<CustomInput placeholder="Nhập ở đây" />;
```

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*WUSmPn_ycbXMtfQRXSI6HA.png" alt="" height="273" width="700"><figcaption><p>Kiểu '{ placeholder: string; }' không có thuộc tính nào chung với kiểu 'IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…'.</p></figcaption></figure>

Chúng ta có thể thấy `{ placeholder: string; }`kiểu từ `createElement`câu lệnh ẩn sau hậu trường và `IntrinsicAttributes & …`kiểu cho `CustomInput`props của 's. Chỉ là vì một lý do nào đó mà tôi không biết TypeScript nói "không có thuộc tính chung" thay vì "không tồn tại".

**Cách khắc phục:** Giải quyết sự không nhất quán! Thêm `placeholder`vào `CustomInputProps`hoặc xóa nó khỏi `<CustomInput … />`.

## Phù! <a href="#id-3a21" id="id-3a21"></a>

Đây rồi. Giải thích về các lỗi TypeScript phổ biến nhất mà bạn gặp phải trong React JSX và cách bạn có thể giải quyết chúng. Hy vọng điều đó sẽ làm giảm bớt sự tức giận trong tương lai khi lập trình.

Tôi thừa nhận rằng việc tìm ra những cách mới để kiểm tra kiểu để bắt lỗi là một trong những bài tập lập trình yêu thích của tôi. Trong [cơ sở mã Boards and Commissions](https://github.com/CityOfBoston/commissions) mới , chúng tôi đang sử dụng [`ts2gql`](https://github.com/convoyinc/ts2gql)để biến giao diện TypeScript thành lược đồ GraphQL, nghĩa là chúng tôi có thể sử dụng các giao diện TypeScript đó để [kiểm tra kiểu rất chính xác cho trình phân giải của mình](https://github.com/CityOfBoston/commissions/blob/develop/modules-js/graphql-typescript/src/graphql-typescript.ts) . Cũng giống như với ứng dụng web Registry, chúng tôi đang sử dụng [`apollo-codegen`](https://github.com/apollographql/apollo-codegen)để tạo đối số và kiểu đầu ra cho các truy vấn GraphQL và chúng tôi cũng đã thêm vào [`sql-ts`](https://github.com/rmp135/sql-ts)để tạo kiểu TypeScript từ các bảng SQL Server.

Đây là lời nhắc nhở hữu ích đối với tôi và bất kỳ ai đang lắp ráp dụng cụ, rằng lan can bảo vệ chỉ hiệu quả khi bạn biết nó đang bảo vệ cái gì.

## Lời cảm ơn <a href="#id-58a1" id="id-58a1"></a>

[Guillaume Marceau](http://gmarceau.qc.ca/) , khi còn là sinh viên sau đại học, đã giúp tôi hiểu cách lỗi kiểu phát sinh từ hai phần xung đột của một chương trình. James Duffy đã hiệu đính bài viết này.

Và tất nhiên là cảm ơn John vì đã vượt qua tất cả những điều này một cách mạnh mẽ.

## Deciphering TypeScript’s React errors <a href="#id-7314" id="id-7314"></a>

[![Fiona Hopkins](https://miro.medium.com/v2/resize:fill:44:44/1*J5xY58uqhfmHfOlTThLDfg.jpeg)](https://medium.com/@fionawhim?source=post_page---byline--8704cc9ef402--------------------------------)[![Innovation and Technology](https://miro.medium.com/v2/resize:fill:24:24/1*_JLNKx2yqO48v9JV-Py0Ng.png)](https://medium.com/innovation-and-technology?source=post_page---byline--8704cc9ef402--------------------------------)

[Fiona Hopkins](https://medium.com/@fionawhim?source=post_page---byline--8704cc9ef402--------------------------------)

·

Follow

Published in[Innovation and Technology](https://medium.com/innovation-and-technology?source=post_page---byline--8704cc9ef402--------------------------------)·16 min read·Jun 28, 2018

2.2K

11

Static type checking is one of my favorite engineering guard rails, which is why I made adopting a typechecker a priority when [choosing the toolset](https://medium.com/innovation-and-technology/bringing-death-certificates-to-life-with-agile-tools-dcb0174cdc1a) for webapp development on the [City of Boston’s Digital team](https://www.boston.gov/digital).

Static type checking de-risks software maintenance, since it reveals places where you need to update code to accommodate changes you’re making. It’s equally good for new development since, with good editor support, it ensures you’re calling functions that exist and are giving them the arguments they expect.

But, last Friday, I got a DM from my co-worker John, who has been writing some React components:

> I hate those errors, I was going to say dislike but naaaaahhh I hate them lol

John’s a recent bootcamp grad, working with me for the summer as a fellow. He’s also super-chill, so seeing this strong reaction really woke me up.

*I had thrown John into a type-checking world without preparation.* He fell victim to a **paradox of guard rails**: it can take more effort to deal with a checker’s “helpful” error messages than it would ever be to debug the problems it’s warning you against.

The combination of React, JSX, and the DOM make for some hairy TypeScript types. When TypeScript complains, its error messages are verbose, with a lot of names you don’t recognize from your own code. So, for John and everyone else new to this environment, I present my guide to decoding your React / TypeScript error messages.

(You might notice that the Registry webapp was written with [Flow](https://flow.org/). For new development we’re working in [TypeScript](https://www.typescriptlang.org/).)

## Why even typecheck React? <a href="#id-27c1" id="id-27c1"></a>

Let’s see what we hope to get out of using TypeScript with React in the first place. When things go smoothly, we’ll be catching these bugs:

* Trying to pass a prop that a component doesn’t want
* Forgetting to pass a prop that a component requires
* Getting a prop’s type wrong, such as passing a string when it expects a number

If we write these bugs, TypeScript will show an error in our editor right away. Without TypeScript, we’d have to catch these bugs later during testing, and it might be tedious debugging to figure out where they come from.

(Whether or not it’s worth it to use a tool to catch these bugs is a question for your team. I find it valuable, which is why we’re here.)

## Errors with DOM elements <a href="#fbaa" id="fbaa"></a>

For starters, let’s look at using standard DOM elements in JSX. TypeScript will check to make sure that every attribute you put on an HTML tag exists and is of the right type. For example:

```
render() {
  return <input type="text" name="color" />;
}
```

The above React/JSX code type checks fine, because `<input>`s have both `type` and `name` attributes! You won’t see any errors. TypeScript can do this because there’s a React library (`@types/react` ) that defines all HTML elements and what attributes they each take.

**Note:** In this guide I use “attribute” and “property” pretty much interchangeably. “Attribute” is what they’re called in HTML, but on React components they’re “props.” And, when they’re on a JavaScript Object, TypeScript calls them “properties.”

### Using a property that doesn’t exist <a href="#id-325c" id="id-325c"></a>

Now let’s mis-spell “`name`” and see what happens:

<pre><code>render() {
<strong>  return &#x3C;input type="text" nmae="color" />;
</strong>}
</code></pre>

If you have TypeScript support in your editor, you should get a red underline on that line, and the following error message:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*XDmLfWXuRKhTkjFjV9HjuQ.png" alt="" height="144" width="700"><figcaption><p>“Property ‘nmae’ does not exist on type ‘DetailedHTMLProps&#x3C;InputHTMLAttributes&#x3C;HTMLInputElement>, HTMLInputElement>’”</p></figcaption></figure>

That looks a little daunting, but we can break it down piece-by-piece:

* `Property 'nmae'` — The “properties” are the attributes on the `<input>` tag. So this error is about “`nmae=”…"`”.
* `does not exist on type` — There’s a “type,” a definition somewhere of what is allowed to be on `<input>`, and “`nmae`” isn’t on the list.
* `'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'` — OK, this is a doozy. This describes how TypeScript thinks of`<input>`’s attributes. All of these names (`DetailedHTMLProps`, `InputHTMLAttributes`, and `HTMLInputElement`) are defined in that React library. `DetailedHTMLProps` means attributes that any HTML element can have (like `id`, `tabIndex`, and `style`) and `InputHTMLAttributes` are for ones specific to `<input>` elements ( `name`, `value`, *\&c.*). This is boilerplate you can typically gloss over, but the `HTML***Element` part can be a useful clue in deciphering this.

**The fix:** When you see “`Property XXX does not exist on type`,” it means you’re adding an `XXX="…"` to an element that doesn’t want an `XXX` on it. While that is not itself a problem—the element would just ignore attributes it doesn’t recognize—it’s usually a sign of a bug. In this case, it’s not a problem to add a `nmae`, but it is a problem that we *thought* we were adding a `name` but didn’t actually.

When you see this, check for typos, and check the HTML docs to make sure you’ve got the attribute or element right for what you’re trying to do.

### Getting a property’s type wrong <a href="#id-7fe4" id="id-7fe4"></a>

There’s another error that can come up with HTML: using the wrong type for an attribute. Let’s add a `size` to the `<input>` and see what happens:

<pre><code>render() {
<strong>  return &#x3C;input type="text" name="color" size="6" />;
</strong>}
</code></pre>

This is exactly how you’d write it in an HTML page, but TypeScript shows an error:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*mEy2LH0UmjN17kSsm64chw.png" alt="" height="145" width="700"><figcaption><p>Types of property ‘size’ are incompatible. Type ‘string’ is not assignable to type ‘number | undefined’.</p></figcaption></figure>

* `Types of property 'size' are incompatible.` — We’re talking about `size` now. Note how it says “types” with a plural. That’s because *all type errors are **inconsistencies**.* This error is between two types: the one from our code, and the one from React’s `<input>` definition (remember the`DetailedHTMLProps<…>` bit we saw in the last error).
* `Type 'string'` — This is the first of the types, the one for the argument that we wrote. `"6"` *is* a string, because *all* quoted JSX attributes are strings.
* `is not assignable to`— This is a nuanced way of saying “isn’t.” It’s clarifying what is “incompatible” about the two types.
* `type 'number | undefined'` — This is the second of the two types, the one that the React library said is right for `<input>`’s `size` attribute. The `|` is known as a “union” operator, but you can read it as “or”: “`size` must be a number or undefined.”

**The fix:** We need to change one or the other of the types to make them consistent. Since we can’t change the React library to accept a string, we have to change our code to pass a number:

```
render() {
  <input type="text" name="color" size={6} />
}
```

Remember that `{}` is the way to make a JavaScript expression in JSX. Now instead of a string, we’re providing a number, and the TypeScript error goes away.

The error message said that the type from the React library was `'number | undefined'`. “Undefined” is the same as leaving it off, which is why our previous examples typechecked without a `size`. But this explains why the error message said “is not assignable to” instead of “isn’t.” `6` is a number, not a “number or undefined.” But, a number is *assignable* to a “number or undefined,” because of the “or.”

The React type library doesn’t require any attributes on HTML elements, so there’s no “forgetting to pass a prop that a component requires” error in this section. That will come up when we deal with custom components.

## All type errors are inconsistencies <a href="#id-97e6" id="id-97e6"></a>

I mentioned this above in the breakdown of the `size` bug, but it’s an important enough concept that I want to call it out:

> *All type errors are **inconsistencies**.*

What I mean by this is those red TypeScript squiggles are there because one part of your code doesn’t agree with another part. Which part is right? Your job is to figure that out by thinking about what you’re trying to make your code do.

One of the reasons that type errors can be confusing to interpret is that TypeScript will only underline *one* of the places where the inconsistency is happening. The first example below is an error where TypeScript calls out something in the JSX, but that part is actually right and it’s the type definition that we decide is wrong.

## Errors with custom React components <a href="#id-4ae0" id="id-4ae0"></a>

The principles of typechecking custom components are basically the same as DOM elements, but the names of the types get more confusing.

For this section, we’ll use the following sample component:

```
class CustomInput extends React.Component {
  render() {
    return <input />;
  }
}
```

### Using your own props <a href="#d061" id="d061"></a>

Say we’re using this to build a form, so we need to pass in a `name` that can go on that `<input>`. In React, that means adding a prop. Here’s the new `render` method:

<pre><code>render() {
<strong>  return &#x3C;input name={this.props.fieldName} />
</strong>}
</code></pre>

Which might get used by another component like this:

```
render() {
  return (
    <form>
      <CustomInput fieldName="colorField" />
    </form>
  );
}
```

Seems straightforward, and in plain JavaScript this case would totally work, no bugs. But TypeScript has found something to complain about:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*a3gaehQXBm739VbNZ1sDQw.png" alt="" height="162" width="700"><figcaption><p>Property ‘fieldName’ does not exist on type ‘Readonly&#x3C;{ children?: ReactNode; }> &#x26; Readonly&#x3C;{}>’.</p></figcaption></figure>

* `Property 'fieldName'`— We saw this language before with the `<input>`’s `nmae` attribute, but now it’s referring to a “property” on the `this.props` object, rather than one on an HTML tag.
* `does not exist on type` — This is also the same language. TypeScript has an opinion about what properties are and are not on `this.props`, and `fieldName` is in the “not” pile.
* `'Readonly<{ children?: ReactNode; }> & Readonly<{}>'`— OK, this is hard to read. This is the type that TypeScript thinks of when we say `this.props`.

That last bit needs a more detailed explanation:

* The `Readonly`s mean that you can’t change `this.props`. You shouldn’t be doing that with React, so TypeScript enforces it.
* `{ children?: ReactNode; }` is there because all React components *can* have a `this.props.children` value. `ReactNode` is anything that React knows how to render: strings, components, and so on. The `?` means that this property is optional—you don’t need to pass children to `CustomInput`.
* `&` in TypeScript makes an “intersection” between two types, which is kind of like an object that has all the properties from each of them merged together. So, `this.props` has a type that’s made up of all the properties of `{ children?: ReactNode; }` combined with all the properties of `{}`. Which doesn’t have any.

That comes together to mean that `this.props` has an optional `children` property, but no `fieldName` property.

**The fix:** To fix this, we actually have to make a choice. Say it with me now:

> *All type errors are **inconsistencies**.*

Two things don’t agree. In your `render` method you’re trying to use `this.props.fieldName`. But when you declared your component, you didn’t give it a `fieldName` prop. Which is right? The `render` method or the component declaration?

With DOM elements, we knew that the expected attributes were correct because they came from a library we trust. But this is our own code, so it’s up to us to pick what to change to make the error go away. That means either stop trying to use `this.props.fieldName`, or add `fieldName` to our component’s declaration.

By default, all `React.Component`s have a `this.props` type of `{}`, which we saw in the `&` statement in the error. Since we really do want to have a `fieldName` prop, we have to add it:

<pre><code><strong>interface CustomInputProps {
</strong><strong>  fieldName: string;
</strong><strong>}class CustomInput extends React.Component&#x3C;CustomInputProps> {
</strong>  render() {
    return &#x3C;input name={this.props.fieldName} />;
  }
}
</code></pre>

What’s going on here? We start with `interface`, which is how we make a new “object type” in TypeScript. We’re saying that any object of type `CustomInputProps` must be “an object with a `fieldName` property, which must be a string.”

### Aside: Why are we talking about object types? <a href="#fc6b" id="fc6b"></a>

`CustomInputProps` is an object type, which means that it describes the “shape” of a JavaScript object. For example:

```
const obj: CustomInputProps = {
  fieldName: 'myInput',
};
```

That’s a `const` called `obj`, and `: CustomInputProps` tells TypeScript that its value needs to match the `CustomInputProps` shape. We initialize it with an object literal that has the necessary `fieldName` property, so it typechecks!

Let’s leave off the `fieldName` property and see what happens:

<pre><code><strong>const obj: CustomInputProps = {};
</strong></code></pre>

`{}` doesn’t match `CustomInputProps`, so here’s how TypeScript complains:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*L04lT463InLLj3gWGt1sWA.png" alt="" height="193" width="700"><figcaption><p>Type ‘{}’ is not assignable to type ‘CustomInputProps’. Property ‘fieldName’ is missing in type ‘{}’.</p></figcaption></figure>

* `Type '{}'`— This is TypeScript describing the type of the value that we’re trying to assign to `obj`: an object with no fields.
* `is not assignable to`— Our old friend “is not assignable to.” We’re trying to make an assignment: assigning `{}` to `obj`, but TypeScript is saying we can’t do it.
* `type 'CustomInputProps'.`— This is here because it’s what we declared `obj` to be. Therefore it’s the type that’s being assigned *to*.
* `Property 'fieldName' is missing in type '{}'.`— Now TypeScript is being specific about why an object of type `{}` is not assignable to something of type `CustomInputProps`: There’s no `fieldName`, and, because of the `interface` declaration from above, all `CustomInputProps` objects must have a `fieldName`.

Now let’s see what happens when we put back `fieldName`, but also add a field that’s not part of the `CustomInputProps` type:

<pre><code>const obj: CustomInputProps = {
  fieldName: 'myInput',
<strong>  placeholder: 'Type something here',
</strong>};
</code></pre>

That’s not allowed:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*4yxfnrzn640KhdGX33YxWw.png" alt="" height="236" width="700"><figcaption><p>Type ‘{ fieldName: string; placeholder: string; }’ is not assignable to type ‘CustomInputProps’. Object literal may only specify known properties, and ‘placeholder’ does not exist in type ‘CustomInputProps’.</p></figcaption></figure>

* `Type '{ fieldName: string; placeholder: string; }'`— This is the new type of our object literal. See how it now has the `placeholder` property.
* `is not assignable to type 'CustomInputProps'.` — Same as before. TypeScript thinks this assignment is illegal.
* `Object literal may only specify known properties, and 'placeholder' does not exist in type 'CustomInputProps'.` — This is the explanation for why this case is “not assignable.” `CustomInputProps` doesn’t have a `placeholder` property declared in its `interface` block.

This is a similar case to when we put `nmae` on an `<input>`. Adding the extra `placeholder` property is not in itself a bug, since code working on `CustomInputProps` would ignore it, but it’s a *sign* that there’s probably a bug.

Back to why we’re even talking about object types in the first place. What do they have to do with React props? Objects have `{}` squiggly braces, not the `<>` angle brackets of JSX. To explain this, let’s go back to our DOM element example and talk a bit about JSX.

```
<input name="color" size={6} />
```

Browsers can’t understand this JSX on their own, they only understand plain JavaScript. We typically use the TypeScript compiler or a tool like [Babel](https://babeljs.io/) to take the JSX and make JavaScript out of it. Here’s what that code looks like as JavaScript:

```
React.createElement('input', {
  name: 'color',
  size: 6,
});
```

Hey, those `name="color"` and `size={6}` attributes are an object literal now! The first argument to `createElement` is what we’re creating—a class for custom components, a tag name for HTML elements—and the second argument are the props to pass to it.

This is why TypeScript is always talking about props as object types. When it type checks the React code, it makes sure that that second argument to `createElement` has a type that matches (“is assignable to”) the declared type of the component’s props.

(Because it just transforms things into plain JavaScript, JSX is sometimes referred to as “syntactic sugar.” It’s not actually necessary—we could write those `createElement` statements ourselves—but it makes the code sweeter.)

### Now back to declaring props types <a href="#d888" id="d888"></a>

Let’s remember where we were:

<pre><code><strong>interface CustomInputProps {
</strong><strong>  fieldName: string;
</strong><strong>}class CustomInput extends React.Component&#x3C;CustomInputProps> {
</strong>  render() {
    return &#x3C;input name={this.props.fieldName} />;
  }
}
</code></pre>

Now that we’ve declared the `interface` we want—the “shape” of our component’s props—we need to actually tell TypeScript to use it for `CustomInput`. That’s where the the`React.Component<CustomInputProps>` comes in. The `<…>` bits let us pass a *type parameter*. Think of it as a way of tweaking `React.Component`, kind of like how you use `(…)`s to pass arguments to functions. `ReactComponent` uses its first type parameter as the type for `this.props`, replacing the default `{}`.

We can go back to our `render` method and see what `this.props` is now. In Visual Studio Code, you can hover over it:

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*bUAo19cXzh4ycUKvLW93xQ.png" alt="" height="162" width="700"><figcaption><p>React.Component&#x3C;CustomInputProps, {}, any>.props: Readonly&#x3C;{ children?: React.ReactNode; }> &#x26; Readonly&#x3C;CustomInputProps></p></figcaption></figure>

* `React.Component<CustomInputProps, {}, any>`— This is Visual Studio telling us what `this` is. It closely matches the `extends React.Component<CustomInputProps>` from our class declaration, just with the default values for `CustomInputProps`’s 2 additional type parameters written out explicitly.
* `.props:`— Because that’s what we’re hovering over.
* `Readonly<{ children?: React.ReactNode; }> & Readonly<CustomInputProps>`— Remember this `Readonly` stuff from before? Now it has `CustomInputProps` instead of `{}`.

Cool! With this change, `this.props` keeps the previous `children` property, but now also has everything from `CustomInputProps`, in particular the `fieldName` property.

### There’s also state <a href="#id-77cf" id="id-77cf"></a>

Using `this.state` is nearly identical to `this.props`, so I won’t go into it in depth. The errors you get from it should be basically the same. To give a component’s state a type, use the second type parameter to `React.Component`, like so:

<pre><code>interface Props {
  fieldName: string;
<strong>}interface State {
</strong><strong>  error: string | null;
</strong><strong>}class CustomInput extends React.Component&#x3C;Props, State> {
</strong><strong>  state = {
</strong><strong>    error: null;
</strong><strong>  };
</strong>}
</code></pre>

The above example also shows initializing `state` with a default value. You could also do this with `this.state = {…}` in the `constructor()` method. I’ve also shortened the interface names to “`Props`” and “`State`” so they don’t wrap. They can actually be anything, as long as you’re consistent between the `interface` and the `React.Component<…>`.

### Required props on a custom component <a href="#id-004e" id="id-004e"></a>

Let’s go back to using our `CustomInput` component. This will be similar to when we were making `<input>` elements.

Remember where we left off with `CustomInput`:

```
interface CustomInputProps {
  fieldName: string;
}class CustomInput extends React.Component<CustomInputProps> {
  …
}
```

Let’s make one!

```
render() {
  return (
    <form>
      <CustomInput />
    </form>
  );
}
```

And the error is…

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*n9ulcUhM2uSzo4-7yq2ZJg.png" alt="" height="346" width="700"><figcaption><p>Type ‘{}’ is not assignable to type ‘IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…’. Type ‘{}’ is not assignable to type ‘Readonly&#x3C;CustomInputProps>’. Property ‘fieldName’ is missing in type ‘{}’.</p></figcaption></figure>

It just keeps getting more complicated, doesn’t it?

* `Type '{}'`— To explain this, remember how JSX works. Our above line of code turns into `React.createElement(CustomInput, {})`. `{}` because we didn’t write any props in the JSX. `Type '{}'` is TypeScript describing that second, props argument.
* `is not assignable to` — The old standby. There’s an inconsistency in the types!
* `'IntrinsicAttributes & IntrinsicClassAttributes<CustomInput> & Readonly<{ children?: ReactNode; }>...'` — This is such a mess that TypeScript literally gives up and stops before it gets to the end. The “intrinsic” bits cover common React props like `key` and `ref`. You can see the `Readonly<{ children?: ReactNode; }>` at the end, which is the start of the `this.props` type we saw before. The complete type here is: `IntrinsicAttributes & IntrinsicClassAttributes<CustomInput> & Readonly<{ children?: ReactNode; }> & Readonly<CustomInputProps>`, but honestly you can glaze over this line.
* `Type '{}' is not assignable to type 'Readonly<CustomInputProps>'`— Here TypeScript calls out what part of the `IntrinsicAttributes & … & …` it’s having trouble with. `{}` is perfectly assignable to `IntrinsicAttributes`, `IntrinsicClassAttributes`, and `Readonly<{ children?: ReactNode; }>` because all of their fields are optional. What it’s not assignable to is our `CustomInputProps`.
* `Property 'fieldName' is missing in type '{}'.`— This is the most useful part of the error message. The `type '{}'` is calling back to that second `createElement` argument, the type of the props from the JSX.

**The fix:** Once again, type errors are flagging *inconsistencies* in the code. As written, `CustomInput` wants a `fieldName` prop, but you’re not giving it one. So, either give it one, or make it not want one in the first place. Let’s look at how we would do each.

To pass `fieldName`, we do this:

<pre><code><strong>&#x3C;CustomInput fieldName="myField" />
</strong></code></pre>

After the JSX translation, that turns into:

<pre><code><strong>React.createElement(CustomInput, { fieldName: "myField" });
</strong></code></pre>

The type of that second, props argument is `{ fieldName: string }` which *is* assignable to `CustomInputProps`.

Here’s the other case, making `fieldName` optional. For this, we need to change the `interface` where `CustomInputProps` was declared.

<pre><code>interface CustomInputProps {
<strong>  fieldName?: string;
</strong>}
</code></pre>

See how there’s a `?` there now? That tells TypeScript that this property is optional. Now both `{ fieldName: string }` and `{}` are assignable to `CustomInputProps`.

This does mean that `this.props.fieldName` now has the type `'string | undefined'`, so TypeScript will make you take that into account in the rest of your code. No calling `this.props.fieldName.length` without checking that it’s defined first!

### Passing unknown props <a href="#id-226d" id="id-226d"></a>

Finally, let’s look at the error messages you get when you give a prop to a custom component that isn’t in its props type. TypeScript has two different error messages, depending on what other props you pass.

```
<CustomInput fieldName="myField" placeholder="Type here" />
```

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*4Lrl6y6lXyXmUWjenXCOLg.png" alt="" height="166" width="700"><figcaption><p>Property ‘placeholder’ does not exist on type ‘IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…’.</p></figcaption></figure>

This error message is the same one we got with `nmae` in the early example, just with `CustomInput`’s longer props type at the end. It is **very unfortunate** that this is cut off before it says `CustomInputProps`, because that’s a piece of information that would *really* help track down what’s going on. Just remember that when you see this, it’s most likely that the prop you’re passing it isn’t defined in the component’s own props interface.

Curiously, if we leave off `fieldName`, the error message is different:

```
export interface CustomInputProps {
  fieldName?: string;
}<CustomInput placeholder="Type here" />;
```

<figure><img src="https://miro.medium.com/v2/resize:fit:700/1*WUSmPn_ycbXMtfQRXSI6HA.png" alt="" height="273" width="700"><figcaption><p>Type ‘{ placeholder: string; }’ has no properties in common with type ‘IntrinsicAttributes &#x26; IntrinsicClassAttributes&#x3C;CustomInput> &#x26; Readonly&#x3C;{ children?: ReactNode; }>…’.</p></figcaption></figure>

We can see the `{ placeholder: string; }` type from the `createElement` statement that’s behind the scenes, and the `IntrinsicAttributes & …` type for `CustomInput`’s props. It’s just for a reason I don’t know TypeScript says “has no properties in common” rather than “does not exist.”

**The fix:** Resolve the inconsistency! Either add `placeholder` to `CustomInputProps` or remove it from `<CustomInput … />`.

## Whew! <a href="#id-3a21" id="id-3a21"></a>

There we go. Explanations for the most common TypeScript errors you run into in your React JSX, and how you might solve them. Hopefully that will reduce any future rage when programming.

I will admit that finding new ways for type checking to catch errors is one of my favorite programming exercises. In the new [Boards and Commissions codebase](https://github.com/CityOfBoston/commissions), we’re using [`ts2gql`](https://github.com/convoyinc/ts2gql) to turn TypeScript interfaces into a GraphQL schema, which means we can use those TypeScript interfaces to [very precisely typecheck our resolvers](https://github.com/CityOfBoston/commissions/blob/develop/modules-js/graphql-typescript/src/graphql-typescript.ts). Just as with the Registry webapp, we’re using [`apollo-codegen`](https://github.com/apollographql/apollo-codegen) to generate argument and output types for GraphQL queries, and we’ve also added [`sql-ts`](https://github.com/rmp135/sql-ts) to make TypeScript types from SQL Server tables.

It’s a good reminder to me, and anyone else who’s putting tooling together, that a guard rail is only as good as whether or not you can tell what it’s guarding against.

## Acknowledgements <a href="#id-58a1" id="id-58a1"></a>

[Guillaume Marceau](http://gmarceau.qc.ca/), way back in his grad school days, helped me understand how type errors arise from two conflicting parts of a program. James Duffy proofread this article.

And of course to John, for powering through all of this like a champ.
