React + TypeScript實踐

❗️準備知識 :

  • 熟悉 React

本文檔參考 TypeScript 最新版本git

如何引入React

import * as React from 'react'

import * as ReactDOM from 'react-dom'
複製代碼

這種引用方式被證實是最可靠的一種方式, 推薦使用github

而另一種引用方式:typescript

import React from 'react'

import ReactDOM from 'react-dom'
複製代碼

須要添加額外的配置:"allowSyntheticDefaultImports": true小程序

函數式組件的聲明方式

聲明的幾種方式數組

第一種:也是比較推薦的一種,使用 React.FunctionComponent,簡寫形式:React.FC:promise

// Great

type AppProps = {

  message: string

}



const App: React.FC<AppProps> = ({ message, children }) => (

  <div> {message} {children} </div>

)
複製代碼

使用用React.FC聲明函數組件和普通聲明以及 PropsWithChildren 的區別是:babel

  • React.FC顯式地定義了返回類型,其餘方式是隱式推導的
  • React.FC對靜態屬性:displayName、propTypes、defaultProps提供了類型檢查和自動補全

  • React.FC爲children提供了隱式的類型(ReactElement | null),可是目前,提供的類型存在一些 issue(問題)

好比如下用法 React.FC 會報類型錯誤:

const App: React.FC = props => props.children



const App: React.FC = () => [1, 2, 3]



const App: React.FC = () => 'hello'
複製代碼

解決方法:

const App: React.FC<{}> = props => props.children as any

const App: React.FC<{}> = () => [1, 2, 3] as any

const App: React.FC<{}> = () => 'hello' as any



// 或者



const App: React.FC<{}> = props => (props.children as unknown) as JSX.Element

const App: React.FC<{}> = () => ([1, 2, 3] as unknown) as JSX.Element

const App: React.FC<{}> = () => ('hello' as unknown) as JSX.Element
複製代碼

在一般狀況下,使用 React.FC 的方式聲明最簡單有效,推薦使用;若是出現類型不兼容問題,建議使用如下兩種方式:

第二種:使用 PropsWithChildren,這種方式能夠爲你省去頻繁定義 children 的類型,自動設置 children 類型爲 ReactNode:

type AppProps = React.PropsWithChildren<{ message: string }>



const App = ({ message, children }: AppProps) => (

  <div> {message} {children} </div>

)
複製代碼

第三種:直接聲明:

type AppProps = {

  message: string

  children?: React.ReactNode

}



const App = ({ message, children }: AppProps) => (

  <div> {message} {children} </div>

)
複製代碼

Hooks

useState<T>

大部分狀況下,TS 會自動爲你推導 state 的類型:

// `val`會推導爲boolean類型, toggle接收boolean類型參數

const [val, toggle] = React.useState(false)



// obj會自動推導爲類型: {name: string}

const [obj] = React.useState({ name: 'sj' })



// arr會自動推導爲類型: string[]

const [arr] = React.useState(['One', 'Two'])
複製代碼

使用推導類型做爲接口/類型:

export default function App() {

  // user會自動推導爲類型: {name: string}

  const [user] = React.useState({ name: 'sj', age: 32 })

  const showUser = React.useCallback((obj: typeof user) => {

    return `My name is ${obj.name}, My age is ${obj.age}`

  }, [])



  return <div className="App">用戶: {showUser(user)}</div>

}
複製代碼

可是,一些狀態初始值爲空時(null),須要顯示地聲明類型:

type User = {

  name: string

  age: number

}



const [user, setUser] = React.useState<User | null>(null)
複製代碼

useRef<T>

當初始值爲 null 時,有兩種建立方式:

const ref1 = React.useRef<HTMLInputElement>(null)

const ref2 = React.useRef<HTMLInputElement | null>(null)
複製代碼

這兩種的區別在於

  • 第一種方式的 ref1.current 是只讀的(read-only),而且能夠傳遞給內置的 ref 屬性,綁定 DOM 元素
  • 第二種方式的 ref2.current 是可變的(相似於聲明類的成員變量)
const ref = React.useRef(0)



React.useEffect(() => {

  ref.current += 1

}, [])
複製代碼

這兩種方式在使用時,都須要對類型進行檢查:

const onButtonClick = () => {

  ref1.current?.focus()



  ref2.current?.focus()

}
複製代碼

在某種狀況下,能夠省去類型檢查,經過添加 ! 斷言不推薦

// Bad

function MyComponent() {

  const ref1 = React.useRef<HTMLDivElement>(null!)

  React.useEffect(() => {

    // 不須要作類型檢查,須要人爲保證ref1.current.focus必定存在

    doSomethingWith(ref1.current.focus())

  })

  return <div ref={ref1}> etc </div>

}
複製代碼

useEffect

useEffect 須要注意回調函數的返回值只能是函數或者 undefined

function App() {

  // undefined做爲回調函數的返回值

  React.useEffect(() => {

    // do something...

  }, [])



  // 返回值是一個函數

  React.useEffect(() => {

    // do something...

    return () => {}

  }, [])

}
複製代碼

useMemo<T> / useCallback<T>

useMemouseCallback 均可以直接從它們返回的值中推斷出它們的類型

useCallback 的參數必須制定類型,不然ts不會報錯,默認指定爲 any

const value = 10

// 自動推斷返回值爲 number

const result = React.useMemo(() => value * 2, [value])



// 自動推斷 (value: number) => number

const multiply = React.useCallback((value: number) => value * multiplier, [

  multiplier,

])
複製代碼

同時也支持傳入泛型, useMemo 的泛型指定了返回值類型,useCallback 的泛型指定了參數類型

// 也能夠顯式的指定返回值類型,返回值不一致會報錯

const result = React.useMemo<string>(() => 2, [])

// 類型「() => number」的參數不能賦給類型「() => string」的參數。



const handleChange = React.useCallback<

  React.ChangeEventHandler<HTMLInputElement>

>(evt => {

  console.log(evt.target.value)

}, [])
複製代碼

自定義Hooks

須要注意,自定義 Hook 的返回值若是是數組類型,TS 會自動推導爲 Union 類型,而咱們實際須要的是數組裏裏每一項的具體類型,須要手動添加 const 斷言 進行處理:

function useLoading() {

  const [isLoading, setState] = React.useState(false)

  const load = (aPromise: Promise<any>) => {

    setState(true)

    return aPromise.then(() => setState(false))

  }



  // 實際須要: [boolean, typeof load] 類型

  // 而不是自動推導的:(boolean | typeof load)[]

  return [isLoading, load] as const

}
複製代碼

若是使用 const 斷言遇到問題,也能夠直接定義返回類型:

export function useLoading(): [ boolean, (aPromise: Promise<any>) => Promise<any> ] {

  const [isLoading, setState] = React.useState(false)

  const load = (aPromise: Promise<any>) => {

    setState(true)

    return aPromise.then(() => setState(false))

  }

  return [isLoading, load]

}
複製代碼

若是有大量的自定義 Hook 須要處理,這裏有一個方便的工具方法能夠處理 tuple 返回值:

function tuplify<T extends any[]>(...elements: T) {

  return elements

}



function useLoading() {

  const [isLoading, setState] = React.useState(false)

  const load = (aPromise: Promise<any>) => {

    setState(true)

    return aPromise.then(() => setState(false))

  }



  // (boolean | typeof load)[]

  return [isLoading, load]

}



function useTupleLoading() {

  const [isLoading, setState] = React.useState(false)

  const load = (aPromise: Promise<any>) => {

    setState(true)

    return aPromise.then(() => setState(false))

  }



  // [boolean, typeof load]

  return tuplify(isLoading, load)

}
複製代碼

默認屬性defaultProps

大部分文章都不推薦使用 defaultProps , 相關討論能夠**參考連接**

推薦方式:使用默認參數值來代替默認屬性:

type GreetProps = { age?: number }

const Greet = ({ age = 21 }: GreetProps) => {

  /* ... */

}
複製代碼

defaultProps類型

TypeScript3.0+ 在默認屬性 的類型推導上有了極大的改進,雖然尚且存在一些邊界case仍然存在問題不推薦使用,若是有須要使用的場景,可參照以下方式:

type IProps = {

  name: string

}

const defaultProps = {

  age: 25,

}



// 類型定義

type GreetProps = IProps & typeof defaultProps



const Greet = (props: GreetProps) => <div></div>

Greet.defaultProps = defaultProps

// 使用

const TestComponent = (props: React.ComponentProps<typeof Greet>) => {

  return <h1 />

}

const el = <TestComponent name="foo" />
複製代碼

Types or Interfaces

在平常的react開發中 interfacetype 的使用場景十分相似

implementsextends 靜態操做,不容許存在一種或另外一種實現的狀況,因此不支持使用聯合類型:

class Point {

  x: number = 2

  y: number = 3

}



interface IShape {

  area(): number

}



type Perimeter = {

  perimeter(): number

}



type RectangleShape = (IShape | Perimeter) & Point



class Rectangle implements RectangleShape {

  // 類只能實現具備靜態已知成員的對象類型或對象類型的交集。

  x = 2

  y = 3

  area() {

    return this.x + this.y

  }

}



interface ShapeOrPerimeter extends RectangleShape {}

// 接口只能擴展使用靜態已知成員的對象類型或對象類型的交集
複製代碼

使用Type仍是Interface?

有幾種經常使用規則:

  • 在定義公共 API 時(好比編輯一個庫)使用 interface,這樣能夠方便使用者繼承接口
  • 在定義組件屬性(Props)和狀態(State)時,建議使用 type,由於 type的約束性更強

interfacetype 在ts中是兩個不一樣的概念,但在 React 大部分使用的 case 中,interfacetype 能夠達到相同的功能效果,typeinterface 最大的區別是:

  • type 類型不能二次編輯,而 interface 能夠隨時擴展
interface Animal {

  name: string

}



// 能夠繼續在原有屬性基礎上,添加新屬性:color

interface Animal {

  color: string

}



/********************************/



type Animal = {

  name: string

}



// type類型不支持屬性擴展

// Error: Duplicate identifier 'Animal'

type Animal = {

  color: string

}
複製代碼

獲取未導出的Type

某些場景下咱們在引入第三方的庫時會發現想要使用的組件並無導出咱們須要的組件參數類型或者返回值類型,這時候咱們能夠經過 ComponentProps/ ReturnType 來獲取到想要的類型。

// 獲取參數類型

import { Button } from 'library' // 可是未導出props type

type ButtonProps = React.ComponentProps<typeof Button> // 獲取props

type AlertButtonProps = Omit<ButtonProps, 'onClick'> // 去除onClick

const AlertButton: React.FC<AlertButtonProps> = props => (

  <Button onClick={() => alert('hello')} {...props} />

)
複製代碼
// 獲取返回值類型

function foo() {

  return { baz: 1 }

}



type FooReturn = ReturnType<typeof foo> // { baz: number }
複製代碼

Props

一般咱們使用 type 來定義 Props,爲了提升可維護性和代碼可讀性,在平常的開發過程當中咱們但願能夠添加清晰的註釋。

如今有這樣一個 type

type OtherProps = {

  name: string

  color: string

}
複製代碼

在使用的過程當中,hover 對應類型會有以下展現

// type OtherProps = {

// name: string;

// color: string;

// }

const OtherHeading: React.FC<OtherProps> = ({ name, color }) => (

  <h1>My Website Heading</h1>

)
複製代碼

增長相對詳細的註釋,使用時會更清晰,須要注意,註釋須要使用 /**/ // 沒法被 vscode 識別

// Great

/** * @param color color * @param children children * @param onClick onClick */

type Props = {

  /** color */

  color?: string

  /** children */

  children: React.ReactNode

  /** onClick */

  onClick: () => void

}



// type Props

// @param color — color

// @param children — children

// @param onClick — onClick

const Button: React.FC<Props> = ({ children, color = 'tomato', onClick }) => {

  return (

    <button style={{ backgroundColor: color }} onClick={onClick}> {children} </button>

  )

}
複製代碼

經常使用Props ts類型

基礎屬性類型

type AppProps = {

  message: string

  count: number

  disabled: boolean

  /** array of a type! */

  names: string[]

  /** string literals to specify exact string values, with a union type to join them together */

  status: 'waiting' | 'success'

  /** 任意須要使用其屬性的對象(不推薦使用,可是做爲佔位頗有用) */

  obj: object

  /** 做用和`object`幾乎同樣,和 `Object`徹底同樣 */

  obj2: {}

  /** 列出對象所有數量的屬性 (推薦使用) */

  obj3: {

    id: string

    title: string

  }

  /** array of objects! (common) */

  objArr: {

    id: string

    title: string

  }[]

  /** 任意數量屬性的字典,具備相同類型*/

  dict1: {

    [key: string]: MyTypeHere

  }

  /** 做用和dict1徹底相同 */

  dict2: Record<string, MyTypeHere>

  /** 任意徹底不會調用的函數 */

  onSomething: Function

  /** 沒有參數&返回值的函數 */

  onClick: () => void

  /** 攜帶參數的函數 */

  onChange: (id: number) => void

  /** 攜帶點擊事件的函數 */

  onClick(event: React.MouseEvent<HTMLButtonElement>): void

  /** 可選的屬性 */

  optional?: OptionalType

}
複製代碼

經常使用React屬性類型

export declare interface AppBetterProps {

  children: React.ReactNode // 通常狀況下推薦使用,支持全部類型 Great

  functionChildren: (name: string) => React.ReactNode

  style?: React.CSSProperties // 傳遞style對象

  onChange?: React.FormEventHandler<HTMLInputElement>

}



export declare interface AppProps {

  children1: JSX.Element // 差, 不支持數組

  children2: JSX.Element | JSX.Element[] // 通常, 不支持字符串

  children3: React.ReactChildren // 忽略命名,不是一個合適的類型,工具類類型

  children4: React.ReactChild[] // 很好

  children: React.ReactNode // 最佳,支持全部類型 推薦使用

  functionChildren: (name: string) => React.ReactNode // recommended function as a child render prop type

  style?: React.CSSProperties // 傳遞style對象

  onChange?: React.FormEventHandler<HTMLInputElement> // 表單事件, 泛型參數是event.target的類型

}
複製代碼

Forms and Events

onChange

change 事件,有兩個定義參數類型的方法。

第一種方法使用推斷的方法簽名(例如:React.FormEvent <HTMLInputElement> :void

import * as React from 'react'



type changeFn = (e: React.FormEvent<HTMLInputElement>) => void



const App: React.FC = () => {

  const [state, setState] = React.useState('')



  const onChange: changeFn = e => {

    setState(e.currentTarget.value)

  }



  return (

    <div> <input type="text" value={state} onChange={onChange} /> </div>

  )

}
複製代碼

第二種方法強制使用 @types / react 提供的委託類型,兩種方法都可。

import * as React from 'react'



const App: React.FC = () => {

  const [state, setState] = React.useState('')



  const onChange: React.ChangeEventHandler<HTMLInputElement> = e => {

    setState(e.currentTarget.value)

  }

  return (

    <div> <input type="text" value={state} onChange={onChange} /> </div>

  )

}
複製代碼

onSubmit

若是不太關心事件的類型,能夠直接使用 React.SyntheticEvent,若是目標表單有想要訪問的自定義命名輸入,可使用類型擴展

import * as React from 'react'



const App: React.FC = () => {

  const onSubmit = (e: React.SyntheticEvent) => {

    e.preventDefault()

    const target = e.target as typeof e.target & {

      password: { value: string }

    } // 類型擴展

    const password = target.password.value

  }

  return (

    <form onSubmit={onSubmit}> <div> <label> Password: <input type="password" name="password" /> </label> </div> <div> <input type="submit" value="Log in" /> </div> </form>

  )

}
複製代碼

Operators

經常使用的操做符,經常使用於類型判斷

  • typeof and instanceof: 用於類型區分
  • keyof: 獲取object的key

  • O[K]: 屬性查找

  • [K in O]: 映射類型

  • + or - or readonly or ?: 加法、減法、只讀和可選修飾符

  • x ? Y : Z: 用於泛型類型、類型別名、函數參數類型的條件類型

  • !: 可空類型的空斷言

  • as: 類型斷言

  • is: 函數返回類型的類型保護

Tips

使用查找類型訪問組件屬性類型

經過查找類型減小 type 的非必要導出,若是須要提供複雜的 type,應當提取到做爲公共 API 導出的文件中。

如今咱們有一個 Counter 組件,須要 name 這個必傳參數:

// counter.tsx

import * as React from 'react'

export type Props = {

  name: string

}

const Counter: React.FC<Props> = props => {

  return <></>

}

export default Counter
複製代碼

在其餘引用它的組件中咱們有兩種方式獲取到 Counter 的參數類型

第一種是經過 typeof 操做符(推薦

// Great

import Counter from './d-tips1'

type PropsNew = React.ComponentProps<typeof Counter> & {

  age: number

}



const App: React.FC<PropsNew> = props => {

  return <Counter {...props} />

}

export default App
複製代碼

第二種是經過在原組件進行導出

import Counter, { Props } from './d-tips1'



type PropsNew = Props & {

  age: number

}



const App: React.FC<PropsNew> = props => {

  return (

    <> <Counter {...props} /> </>

  )

}

export default App
複製代碼

不要在type或interface中使用函數聲明

保持一致性,類型/接口的全部成員都經過相同的語法定義。

--strictFunctionTypes 在比較函數類型時強制執行更嚴格的類型檢查,但第一種聲明方式下嚴格檢查不生效。

✅

interface ICounter {

  start: (value: number) => string

}



❌

interface ICounter1 {

  start(value: number): string

}



🌰

interface Animal {}

interface Dog extends Animal {

  wow: () => void

}

interface Comparer<T> {

  compare: (a: T, b: T) => number

}

declare let animalComparer: Comparer<Animal>

declare let dogComparer: Comparer<Dog>

animalComparer = dogComparer // Error

dogComparer = animalComparer // Ok



interface Comparer1<T> {

  compare(a: T, b: T): number

}

declare let animalComparer1: Comparer1<Animal>

declare let dogComparer1: Comparer1<Dog>

animalComparer1 = dogComparer // Ok

dogComparer1 = animalComparer // Ok
複製代碼

事件處理

咱們在進行事件註冊時常常會在事件處理函數中使用 event 事件對象,例如當使用鼠標事件時咱們經過 clientXclientY 去獲取指針的座標。

你們可能會想到直接把 event 設置爲 any 類型,可是這樣就失去了咱們對代碼進行靜態檢查的意義。

function handleEvent(event: any) {

  console.log(event.clientY)

}
複製代碼

試想下當咱們註冊一個 Touch 事件,而後錯誤的經過事件處理函數中的 event 對象去獲取其 clientY 屬性的值,在這裏咱們已經將 event 設置爲 any 類型,致使 TypeScript 在編譯時並不會提示咱們錯誤, 當咱們經過 event.clientY 訪問時就有問題了,由於 Touch 事件的 event 對象並無 clientY 這個屬性。

經過 interfaceevent 對象進行類型聲明編寫的話又十分浪費時間,幸運的是 React 的聲明文件提供了 Event 對象的類型聲明。

Event 事件對象類型

  • ClipboardEvent<T = Element> 剪切板事件對象
  • DragEvent<T =Element> 拖拽事件對象

  • ChangeEvent<T = Element> Change事件對象

  • KeyboardEvent<T = Element> 鍵盤事件對象

  • MouseEvent<T = Element> 鼠標事件對象

  • TouchEvent<T = Element> 觸摸事件對象

  • WheelEvent<T = Element> 滾輪時間對象

  • AnimationEvent<T = Element> 動畫事件對象

  • TransitionEvent<T = Element> 過渡事件對象

事件處理函數類型

當咱們定義事件處理函數時有沒有更方便定義其函數類型的方式呢?答案是使用 React 聲明文件所提供的 EventHandler 類型別名,經過不一樣事件的 EventHandler 的類型別名來定義事件處理函數的類型

type EventHandler<E extends React.SyntheticEvent<any>> = {

  bivarianceHack(event: E): void

}['bivarianceHack']

type ReactEventHandler<T = Element> = EventHandler<React.SyntheticEvent<T>>

type ClipboardEventHandler<T = Element> = EventHandler<React.ClipboardEvent<T>>

type DragEventHandler<T = Element> = EventHandler<React.DragEvent<T>>

type FocusEventHandler<T = Element> = EventHandler<React.FocusEvent<T>>

type FormEventHandler<T = Element> = EventHandler<React.FormEvent<T>>

type ChangeEventHandler<T = Element> = EventHandler<React.ChangeEvent<T>>

type KeyboardEventHandler<T = Element> = EventHandler<React.KeyboardEvent<T>>

type MouseEventHandler<T = Element> = EventHandler<React.MouseEvent<T>>

type TouchEventHandler<T = Element> = EventHandler<React.TouchEvent<T>>

type PointerEventHandler<T = Element> = EventHandler<React.PointerEvent<T>>

type UIEventHandler<T = Element> = EventHandler<React.UIEvent<T>>

type WheelEventHandler<T = Element> = EventHandler<React.WheelEvent<T>>

type AnimationEventHandler<T = Element> = EventHandler<React.AnimationEvent<T>>

type TransitionEventHandler<T = Element> = EventHandler<

  React.TransitionEvent<T>

>
複製代碼

bivarianceHack 爲事件處理函數的類型定義,函數接收一個 event 對象,而且其類型爲接收到的泛型變量 E 的類型, 返回值爲 void

關於爲什麼是用bivarianceHack而不是(event: E): void,這與strictfunctionTypes選項下的功能兼容性有關。(event: E): void,若是該參數是派生類型,則不能將其傳遞給參數是基類的函數。

class Animal {

  private x: undefined

}

class Dog extends Animal {

  private d: undefined

}



type EventHandler<E extends Animal> = (event: E) => void

let z: EventHandler<Animal> = (o: Dog) => {} // fails under strictFunctionTyes



type BivariantEventHandler<E extends Animal> = {

  bivarianceHack(event: E): void

}['bivarianceHack']

let y: BivariantEventHandler<Animal> = (o: Dog) => {}
複製代碼

Promise 類型

在作異步操做時咱們常用 async 函數,函數調用時會 return 一個 Promise 對象,可使用 then 方法添加回調函數。Promise<T> 是一個泛型類型,T 泛型變量用於肯定 then 方法時接收的第一個回調函數的參數類型。

type IResponse<T> = {

  message: string

  result: T

  success: boolean

}



async function getResponse(): Promise<IResponse<number[]>> {

  return {

    message: '獲取成功',

    result: [1, 2, 3],

    success: true,

  }

}



getResponse().then(response => {

  console.log(response.result)

})
複製代碼

首先聲明 IResponse 的泛型接口用於定義 response 的類型,經過 T 泛型變量來肯定 result 的類型。而後聲明瞭一個 異步函數 getResponse 而且將函數返回值的類型定義爲 Promise<IResponse<number[]>> 。最後調用 getResponse 方法會返回一個 promise 類型,經過 then 調用,此時 then 方法接收的第一個回調函數的參數 response 的類型爲,{ message: string, result: number[], success: boolean}

泛型參數的組件

下面這個組件的name屬性都是指定了傳參格式,若是想不指定,而是想經過傳入參數的類型去推導實際類型,這就要用到泛型。

const TestB = ({ name, name2 }: { name: string; name2?: string }) => {

  return (

    <div className="test-b"> TestB--{name} {name2} </div>

  )

}
複製代碼

若是須要外部傳入參數類型,只需 ->

type Props<T> = {

  name: T

  name2?: T

}

const TestC: <T>(props: Props<T>) => React.ReactElement = ({ name, name2 }) => {

  return (

    <div className="test-b"> TestB--{name} {name2} </div>

  )

}



const TestD = () => {

  return (

    <div> <TestC<string> name="123" /> </div> ) } 複製代碼

何時使用泛型

當你的函數,接口或者類:

  • 須要做用到不少類型的時候,舉個🌰

當咱們須要一個 id 函數,函數的參數能夠是任何值,返回值就是將參數原樣返回,而且其只能接受一個參數,在 js 時代咱們會很輕易地甩出一行

const id = arg => arg
複製代碼

因爲其能夠接受任意值,也就是說咱們的函數的入參和返回值都應該能夠是任意類型,若是不使用泛型,咱們只能重複的進行定義

type idBoolean = (arg: boolean) => boolean

type idNumber = (arg: number) => number

type idString = (arg: string) => string

// ...
複製代碼

若是使用泛型,咱們只須要

function id<T>(arg: T): T {

  return arg

}



// 或



const id1: <T>(arg: T) => T = arg => {

  return arg

}
複製代碼
  • 須要被用到不少地方的時候,好比經常使用的工具泛型 Partial

功能是將類型的屬性變成可選, 注意這是淺 Partial

type Partial<T> = { [P in keyof T]?: T[P] }
複製代碼

若是須要深 Partial 咱們能夠經過泛型遞歸來實現

type DeepPartial<T> = T extends Function

  ? T

  : T extends object

  ? { [P in keyof T]?: DeepPartial<T[P]> }

  : T



type PartialedWindow = DeepPartial<Window>
複製代碼

字節跳動懂車帝團隊招聘

咱們是字節跳動旗下懂車帝產品線,目前業務上正處於高速發展階段,懂車帝自2017年8月正式誕生,僅三年時間已是汽車互聯網行業第二。

如今前端團隊主流的技術棧是React、Typescript,主要負責懂車帝App、M站、PC站、懂車帝小程序產品矩陣、商業化海量業務,商業數據產品等。咱們在類客戶端、多宿主、技術建站、中後臺系統、全棧、富交互等多種應用場景都有大量技術實踐,致力於技術驅動業務發展,探索全部可能性。

加入懂車帝,一塊兒打造汽車領域最專業最開放的前端團隊!

簡歷直達dcar_fe@bytedance.com

郵件標題:應聘+城市+崗位名稱

相關文章
相關標籤/搜索