[A-00194] Typescript入門

typescript入門用の記事です。

主にdeno,bunを使って実行します。

brew install deno
brew install oven-sh/bun/bun

適当なプロジェクトを作成します。

mkdir ts-project
cd ts-project
deno init
mkdir ts-project
cd ts-project
bun init

・Helloworldしてみる

console.log("Hello,typescript!");

下記のコマンドで実行します。

deno run hello.ts 
$ deno run hello.ts 
Hello,typescript!

・クラスを作ってみる

次はクラスを作ってみます。ディレクトリ構成は下記の通りです。

personクラスを作成します。

export class Person {
    name: string;
    age: number;
    address: string;

    constructor(name: string, age: number, address: string) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    private sayName(): void {
        console.log("My name is " + this.name + "!");
    }

    public getName(): string {
        this.sayName();
        return this.name;
    }
}

次に実行するプログラムを書きます。

import { Person } from './clsdir/person.ts'

const p1 = new Person('takeshi', 14, 'tokyo');
var name:string = p1.getName();

console.log("His name is " + name + ".");

実行すると下記にようになります。

$ deno run main.ts 
My name is takeshi!
His name is takeshi.

・インターフェースを作ってみる

先ほど作成したPersonをインターフェースにしてBusinessPersonクラスを実装してみます。

export interface Person {

    getName():string;

    getAge():number;

    getAddress():string;

    getCountry():string;
}
import { Person } from './person.ts';

export class BusinessPerson implements Person {
    private name: string;
    private age: number;
    protected address: string;
    private country: string;
    private job: string;


    public constructor(name:string, age:number,
        address:string, country:string, job:string) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.country = country;
        this.job = job;
    }

    public introduce(): void {
        console.log("Nice to meet you.");
        console.log("My name is " + this.name +  ". I'm " + this.age + " years old.");
        console.log("I'm now live in " + this.country + ", at " + this.address + ". ");
        console.log("I'm work as " + this.job);
    } 

    public getName(): string {
        return this.name;
    }
    public getAge(): number {
        return this.age;
    }
    public getAddress(): string {
        return this.address;
    }
    public getCountry(): string {
        return this.country;
    }
}

下記が実行プログラムです。

import { BusinessPerson } from './clsdir/business_person.ts'

const bp1 = new BusinessPerson("takeshi", 14 , "tokyo", "Japan", "student");

bp1.introduce();
$ deno run main.ts 
Nice to meet you.
My name is takeshi. I'm 14 years old.
I'm now live in Japan, at tokyo. 
I'm work as student

・Promiseの試しに動かしてみる

promiseとは非同期処理などに使用する関数です。初心者なのでそれだけしか分かりませんが、こいつの中身がどうなっているかを調べるため、下記のコードを実行してみます。

// Promiseの中身
console.log(new Promise(() => {}));

// 成功の場合
console.log(new Promise((resolve, reject) => resolve("Success.")));
// 失敗の場合
console.log(new Promise((resolve, reject) => reject("Failed.")));

// 成功の場合
console.log(new Promise((resolve, reject) => resolve("Success.")).then((text) => console.log( text + "I'm happy.")));
// 失敗の場合
console.log(new Promise((resolve, reject) => reject("Failed.")).catch((text) => console.log( text + "I'm bored.")));
user@usernoMBP test-vite1 % bun run test_main.tsx 
Promise { <pending> }
Promise { <resolved> }
Promise { <rejected> }
Promise { <pending> }
Promise { <pending> }
Success.I'm happy.
Failed.I'm bored.
error: Failed.

・BunでReactを動かす

bunを使ってReactを動かす方法をメモ

bun add react
bun create react-app react-test-app
cd react-test-app
bun start

・TypescriptのReatチュートリアルをやってみる。

TS公式サイトにてReactのチュートリアルがあったのでそれをやってみます。Bunを使用する都合でViteでやってみます。

https://typescriptbook.jp/tutorials/react-like-button-tutorial

bun create vite test-vite1
> React
> Typescript
cd test-vite1
bun install

viteプロジェクトが作成できたら下記のコマンドで動作検証します。

bun run dev

vite+Reactの画面が表示されたらOKです。

src配下のApp.tsxを変更します。

import React, { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function LikeButton() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };
  return ( 
  <span className='likeButton' onClick={handleClick}>
    ❤️ {count}
    </span>
  );
}

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <div className='App'>
        <header className='App-header'>Typescriptはいいぞ</header>
        <br />
        <LikeButton />
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

実行すると下記のように表示されます。

・Design Patternを学ぶ

typescriptでデザインパターンを学習します。

・FactoryMethod


abstract class Creator {
    
    public abstract factoryMethod(): Product;

    public someOperation(): string {
        const product = this.factoryMethod();

        return `Creator: The same creator's code has just worked with ${product.operation()}`;
    }
}

class ConcreteCreator1 extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProduct1();
    }
}

class ConcreteCreator2 extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProduct2();
    }
}

// Interface
interface Product {
    operation(): string;
}

// ConcreteClass of Product
class ConcreteProduct1 implements Product {
    public operation(): string {
        return 'This is first your product.';
    }
}

// ConcreteClass of Product
class ConcreteProduct2 implements Product {
    public operation(): string {
        return 'This is 2nd your product.'
    }
}

function clientCodeFactoryMethod(creator: Creator) {
    console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
    console.log(creator.someOperation());
}

console.log('App: Launched with the ConcreteCreator1.');
clientCodeFactoryMethod(new ConcreteCreator1());
console.log('');

console.log('App: Launched with the ConcreteCreator2.');
clientCodeFactoryMethod(new ConcreteCreator2());
user@usernoMBP ts-catalog % deno factory_method.ts 
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with This is first your product.

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with This is 2nd your product.

・Prototype

class Prototype {

    public primitive: any;
    public component: object;
    public circularReference: ComponentWithBackReference;

    public clone(): this {
        const clone = Object.create(this);

        clone.component = Object.create(this.component);

        clone.circularReference = {
            ...this.circularReference,
            prototype: {...this},
        };

        return clone;
    }
}

class ComponentWithBackReference {
    public prototype;

    constructor(prototype: Prototype) {
        this.prototype = prototype;
    }
}

function clientCodeProtoType() {
    const p1 = new Prototype();
    p1.primitive = 245;
    p1.component = new Date();
    p1.circularReference = new ComponentWithBackReference(p1);

    const p2 = p1.clone();

    if (p1.primitive === p2.primitive) {
        console.log('Primitive field values have been carried over to a clone. Yay!');
    } else {
        console.log('Primitive field values have not been copied. Booo!');
    }

    if (p1.component === p2.component) {
        console.log('Simple component has not been cloned. Booo!');
    } else {
        console.log('Simple component has been cloned. Yay!');
    }

    if (p1.circularReference === p2.circularReference) {
        console.log('Component with back reference has not been cloned. Booo!');
    } else {
        console.log('Component with back reference has been cloned. Yay!');
    }

    if ( p1.circularReference.prototype === p2.circularReference.prototype) {
        console.log('Component with back reference is linked to original object. Booo!');
    } else {
        console.log('Component with back reference is linked to the clone. Yay!');
    }
}

clientCodeProtoType();
user@usernoMBP ts-catalog % deno prototype.ts 
Primitive field values have been carried over to a clone. Yay!
Simple component has been cloned. Yay!
Component with back reference has been cloned. Yay!
Component with back reference is linked to the clone. Yay!

・Singleton

class Singleton {
    static #instance: Singleton;
    static #value: string;

    private constructor() {}

    public static get instance(): Singleton {
        if (!Singleton.#instance) {
            Singleton.#instance = new Singleton();
        }

        return Singleton.#instance;
    }

    public setHello(value: string) {
        Singleton.#value = value;
    }

    public sayHello(): string {
        return Singleton.#value;
    }
}

function clientCodeSingleton() {
    const s1 = Singleton.instance;
    const s2 = Singleton.instance;

    s1.setHello("Guten morgen");

    if (s1 === s2) {
        console.log(
            'Singleton works, both variables contain the same instance.'
        );
        console.log('s1 is ' + s1.sayHello());
        console.log('s2 is ' + s2.sayHello());
    } else {
        console.log('Singleton failed, variables contain different instances.');
    }
}

clientCodeSingleton();
user@usernoMBP src % bun singleton.ts 
Singleton works, both variables contain the same instance.
s1 is Guten morgen
s2 is Guten morgen

・Appendix

参考文献はこちら

https://qiita.com/nogson/items/86b47ee6947f505f6a7b

https://qiita.com/Tsuyoshi84/items/e74109e2ccc0f4e625aa

https://qiita.com/tatsumin0206/items/5835a64cb32ace41e0b1

https://qiita.com/kushidangoAnne/items/85c147aa694e4e6cc008

https://qiita.com/yukiji/items/3db06601ece7f080b0d0

https://zenn.dev/estra/books/ts-the-minimum/viewer/2-tsmini-start-deno-ts

https://hono.dev/getting-started/basic

https://hono.dev/top

https://deno.com

https://bun.sh

https://www.freecodecamp.org/news/learn-typescript-beginners-guide

https://docs.deno.com/runtime/reference/cli/init

https://refactoring.guru/ja/design-patterns/factory-method/typescript/example

https://zenn.dev/kotopasi/articles/7a623823e325b8

https://typescriptbook.jp/tutorials/react-like-button-tutorial

https://zenn.dev/ak/articles/c21609fd3b0fdc

https://zenn.dev/ak/articles/00616eb99523c2

https://zenn.dev/ak/articles/dc23436b241a84

https://zenn.dev/ak/articles/e98737d6156441

https://zenn.dev/ak/articles/ba4c9ace66865e

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

*