TypeScript

박세영·2022년 5월 2일
0

https://www.youtube.com/watch?v=jrKcJxF0lAU

TypeScript is

  • Language build on top of JS
  • superset of JS
  • made by MS

Features

  • catch Error early in development
  • serves as Documents
    coworkers can catch which type was used

Get Started

$ npx create-react-app --template typescript react-with-typescript
set typescript as template
App.js -> App.tsx

typescript when using useState

  1. define new state number using useState
    const [number, setNumber] = useState(5);
  2. define change function for number
    const changeNumber = () => { setNumber("10"); }
    if we try passing string to setNumber, ts will raise error because "10" is not a number

type union

const [number, setNumber] = useState<number | string>(5);
this type allow both number and string to number state

Handling Array of Object as state

  1. set initial sate
const [people, setPeople] = useState([{
  name: "LeBron James",
  url: "",
  age: 36,
  note: "some string aslfiasef"
}]);

If we hover on state people, we can see the type of state.

  1. add another data on state
const [people, setPeople] = useState([
{
  name: "LeBron James",
  url: "",
  age: 36,
  note: "some string aslfiasef"
},
{
  name: "Kobe Bryant",
  url: "",
  age: 42
}
]);

TS automatically specify literal type. the type is an arrray of 2 objects.
It is too specific. So, It needs to be more general.
people.map(person => { console.log(person.height); })
if we access undefined property of object, JS return undefine data. But if we use TS, It raise error because It is not a defined type.

We can define general form of type of state using bracket
const [people, setPeople] = useState<{age: number, name: string, url: string, note: string}[]>([])
But when using interface for defining type, code would be more clean.

interface IState {
  people: {
    name: string
    age: number
    url: string
    note?: string
  }[]
}

question mark on note means that property is optional
interface type can be used like below
const [people, setPeople] = useState<IState["people"]>([])
Interface IState is a object. We can access property people as a index IState["people"]. Looks more clean!👍🏻👍🏻

Then, How can we pass the state as props with ts?

Handling Props

We have to pass parent component's state to child. Make a component named List and give state from App.tsx like below.

<List people={people}/>
An error occurs because there isn't corresponding parameter in child component List.
So, we need to define what type of props we're going to accept from parent. We can use exactly same interface that parent component used before and we can set type of props like below.
const List = (props: IProps) => {} Then, all error gone😎
Both parent and child component check if type is valid.

There is another way to define props's type.
const List: React.FC<IProps> = ({ people }) => {}
Notice that child component List is a React Function Component. We can define the child component's type as React.FC and define props's type in bracket <IProps>. Before we specify what List is, TS has no idea what it is. TS just parse the code as JSX:Element.

Handling Events

Now, we are going to add register feature to the list. We need set of inputs for registering. Set initial state like below

const [input, setInput] = useState({
  name: "",
  age: "",
  note: ""
});

and define handleChange function like below

const handleChange = (e) => {
  setInput({
    ...input,
    [e.target.name]: e.target.value
  })
}

We specify name attribute in input. So, we can control only what we need to handle like [e.target.name]: e.target.value.
We got an error on the parameter e on function handleChange. It can be solved by setting type of e as any but it isn't what we want.

Set onchange property as onChange={(e)=> {}} and hover on e on that. We can get a type of e which is React.ChangeEvent<HTMLInputElement>.
Reset the onchange property as what it was. Just copy and paste the type to parameter e.

When we set a onChange attribute in textarea tag. An error occurs because parameter e is incompatible. log is like below

Type 'ChangeEvent' is not assignable to type 'ChangeEvent'.

It means we have to allow type ChangeEvent<HTMLTextAreaElement> to parameter e. Set type of e as union type
const handleChange = (e :React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>): void => {
Don't forget to set return type is void because it does not return anything.

Now, we have to activate the button add to list. To add person to list, we have to use setPeople which is defined in app.tsx

we can export our interface defined in app.tsx.
add import line import {IState as IProps} from '../App'; in List.tsx

import {IState as Props} from '../App';

We can define nested type in AddToList.tsx

import {IState as Props} from '../App';

interface IProps {
  people: Props["people"]
  setPeople: React.Dispatch<React.SetStateAction<Props["people"]>>
}

handleClick is like below.

const handleClick = (): void => {
    if(
      !input.name ||
      !input.age
    ) {
      return
    }

    setPeople([
      ...people,
      {
        name: input.name,
        age: parseInt(input.age),
        note: input.note
      }
    ])

    setInput({
      name: "",
      age: "",
      note: "",
    })
  }

0개의 댓글