Ajouter TypeScript à un project ReactJS existant

Ceci est un document de note de la transition d’un projet ReactJs qui n’utilisais pas TypeScript à son utilisation dans le projet.
Principalement cela concerne les déclarations de types. Note très informelles qui sont plus de la prise de note du changement qu’autre chose, pour future référence.

import React from 'react';
import './Controls.css';

const Controls = ({ children }) => (
  <div className={"controls"}>
    {children}
  </div>
)

export default Controls;
import { ReactNode } from 'react';
import './Controls.css';

const Controls = ({children} : { children: ReactNode} ) => (
  <div className={"controls"}>
    {children}
  </div>
)

export default Controls;
const Line = ( {x, y, width} : { x: number, y: number, width: number } ) => (
import React from 'react';
import NativeSelect from '@material-ui/core/NativeSelect';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';

const ControlsClef = ( { clef, onChange } ) => {
  clef = clef ? 1 : 0;
  return (
    <div className="control">
      <InputLabel shrink htmlFor="control-clef">Clef</InputLabel><br />
      <NativeSelect value={clef} onChange={onChange} input={<Input name="clef" id="control-clef" />}>
        <option value={0}>Bass</option>
        <option value={1}>Treble</option>
      </NativeSelect>
    </div>
  )
}

export default ControlsClef;
import React, { ChangeEvent } from 'react';
import NativeSelect from '@material-ui/core/NativeSelect';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';

const ControlsClef = ({ clef, onChange }: {clef: boolean, onChange: (event: React.ChangeEvent<HTMLSelectElement>, child: React.ReactNode) => void } ) => {
  return (
    <div className="control">
      <InputLabel shrink htmlFor="control-clef">Clef</InputLabel><br />
      <NativeSelect value={clef} onChange={onChange} input={<Input name="clef" id="control-clef" />}>
        <option value={0}>Bass</option>
        <option value={1}>Treble</option>
      </NativeSelect>
    </div>
  )
}

export default ControlsClef;

ControlsKeyboard

import { Component } from 'react';

class ControlsKeyboard extends Component {
  componentDidMount(){
    document.addEventListener("keydown", this.props.stopRunning, false);
  }
  componentWillUnmount(){
    document.removeEventListener("keydown", this.props.stopRunning, false);
  }
  render(){return null}
}

export default ControlsKeyboard;
import { Component } from 'react';

class ControlsKeyboard extends Component<{ stopRunning: EventListener; }> {
  componentDidMount(){
    document.addEventListener("keydown", this.props.stopRunning, false);
  }
  componentWillUnmount(){
    document.removeEventListener("keydown", this.props.stopRunning, false);
  }
  render(){return null}
}

export default ControlsKeyboard;

Component avec state

export class Home extends Component {
export interface IGlobalState {
  chosenScale: string,
  clef: boolean,
  time: string,
  running: boolean,
  tempo: number
}

export class Home extends Component<{}, IGlobalState> {

Import de fichiers locaux

import C2_note from './octave-2/C2.mp3';
const C2_note = require('./octave-2/C2.mp3');
export class Home extends Component {
    constructor(){
      super();
    }
}
export class Home extends Component {
    constructor(){
      super({});
    }
}
event: KeyboardEvent

Mutliple React Children

import React, { ReactChild } from 'react';

const Mesure = ({ children }: { children: ReactChild }) => (
  <svg>
    {children}
  </svg>
)

export default Mesure;
//
// React Nodes
// http://facebook.github.io/react/docs/glossary.html
// ----------------------------------------------------------------------

type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

On voit que ReactNode peut contenir un ReactFragment qui peut lui aussi contenir un un ReactNodeArray

import React, { ReactNode } from 'react';

const Mesure = ({ children }: { children: ReactNode }) => (
  <svg>
    {children}
  </svg>
)

export default Mesure;

Pour accéder à un index d’object, il faut que sa signature soit définie. Si on veut accéder à signatures[this.state.chosenScale]
Il faut rajouter:

[key: string]: {
  [key: string]: number
}
export interface ISignature {
  sharps: {
    C: number, G: number, D: number, A: number, E: number, B: number, Fs: number, Cs: number
  },
  flats: {
    C: number, F: number ,Bb: number ,Eb: number ,Ab: number ,Db: number ,Gb: number ,Cb: number
  }
  [key: string]: {
    [key: string]: number
  }
}

const signatures: ISignature = {
  sharps: {
    C: 0,
    G: 1,
    D: 2,
    A: 3,
    E: 4,
    B: 5,
    Fs: 6,
    Cs: 7
  },
  flats: {
    C: 0,
    F: 1,
    Bb: 2,
    Eb: 3,
    Ab: 4,
    Db: 5,
    Gb: 6,
    Cb: 7
  }
}

Je me suis rendu compte après qu’il est en fait possible de déclarer la signature en utilisant as comme on a l’habitude de le faire à d’autres endroit du code : as { [key: number]: ReactNode }

TypeScript n’aime que l’on retourne un tableau d’élements s’il n’est pas inséré dans un React.Fragment :

return lines.map(lineProps => <Line {...lineProps} />);
return (
  <React.Fragment>
    { lines.map(lineProps => <Line {...lineProps} />) }
  </React.Fragment>
);
let notes = [];
for(let i = 0; i < 4; i++){
  notes.push({
    x  : (i * staveWidth / 4) + (staveWidth / 8) - MN_centerNote,
    marginTop: marginTop,
    staveHeight: staveHeight,
    key: i,
    index: i,
    sounds: sounds
  });
}
let notes: INoteProps[] = [];
for(let i = 0; i < 4; i++){
  notes.push({
    x  : (i * staveWidth / 4) + (staveWidth / 8) - MN_centerNote,
    marginTop: marginTop,
    staveHeight: staveHeight,
    key: i,
    index: i,
    sounds: sounds
  });
}

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *