普段PHPとJavaScriptを使って開発していますが、TSを触る機会が増えたので勉強したものをまとめてみました。
Primitive タイプについて
PHPとTSが存在するprimitive型
PHP | TypeScript |
---|---|
int | number |
float | number |
string | string |
bool | boolean |
Undefined variable エラーになる | undefined |
mixed | any |
void | void |
objectとarrayについては下にまとめます。
変数定義時の型定義
// 通常の変数定義
$a = 5.5;
// このように定義するとSyntaxエラーが起きる
float $a = 5.5;
const myStr: string = "";
const myNullableStr: string|null = null;
const implicitVal = 5.5; // 自動的にnumber型になる
// typeを使った定義方法
type MyStr = string;
const myStringWithType: MyStr = "";
PHPはTSのように変数定義時に型をつけることができません。関数やクラスなどの定義する場合のみにしか型がつけれられません。
関数について
関数の型定義
function isEmpty(string $a): bool {
return strlen($a) == 0;
}
const isEmpty = (a: string): boolean => {
return a.length == 0;
}
Nullableの定義
function isEmpty(?string $a): bool {
return strlen($a) == 0;
}
// もしくは
function isEmpty(string $a = null): bool {
return strlen($a) == 0;
}
const isEmpty = (a?: string): boolean => {
return a?.length == 0;
}
PHP8から始まる結合型 (Union Type)
function myFunc(string|int|float $a) {}
const myFunc = (a: string|number) => {}
TS特有のtype predicateについて
PHPにない機能ですが、TSでは引数で定義している型と違う型に上書きすることができます。用途として引数に複数の型が存在してフィルターしたり、特定の型を抽出したい場合に役に立ちます。
例として、nullや数字など混合した配列から2文字のアルファベットを抽出したい場合。
const list = ["A","BC",null, 1, "DEF"];
const isTwoAlphabets = (val: any): val is string => {
// TSはval引数をstringとして認識するが、実際はnullや数字などの可能性があるため
// typeofを使ってstring型を抽出する必要がある
if (typeof val !== 'string') {
return false;
}
// 型の抽出が終わったら安全にTSの指示通りに書くことができる
return !!val.match(/^[A-Za-z]{2}$/);
}
const result = list.filter(val => isTwoAlphabets(val));
// 結果は ["BC"]
Object と Class について
PHP8から使える constructor promotion を使って次のように表現します。
class Circle {
public function __construct(
public int|float $radius
) {}
}
class Color {
public function __construct(
public string $name,
public ?string $comment = null
) {}
}
class ColorfulCircle {
public function __construct(
public Circle $circle,
public Color $color
) {}
}
$myCircle = new Circle(5);
$red = new Color("red");
$myRedCircle = new ColorfulCircle($myCircle, $red);
tsの場合は次のようにobjectの型定義ができます。
type Circle = {
radius: number;
};
type Color = {
name: string;
comment?: string;
};
type ColorfulCircle = {
color: Color;
circle: Circle;
}
const myCircle: Circle = {
radius: 5
};
const red: Color = {
name: "red",
};
const myRedCircle: ColorfulCircle = {
circle: myCircle,
color: red
};
補足1、JavaScriptのObjectはPHPのclassのようにprivateやprotectedなど修飾子が存在しないため、定義したobjectのattributeが常にpublicになります。
補足2、TSではtype
の記述の他にinterface
の記述方法もあります。
interface Circle {
radius: number;
}
interface Color {
name: string;
comment?: string;
}
interface ColorfulCircle {
color: Color;
circle: Circle;
}
Extendsと交差型について
PHPではextendsを使ってclassを拡張します。
class ColorfulCircleService extends ColorfulCircle {
public function draw(Circle $circle): void {
print_r("Drawing");
}
}
TSの場合は交差型を使って同じことを実現します。
type ColorfulCircleService = ColorfulCircle & {
draw: (circle: Circle) => void;
};
const myRedCircle: ColorfulCircleService = {
circle: myCircle,
color: red,
draw: (circle: Circle, color: Color) => {
console.log(`Drawing ${color.name} ${circle.radius} circle`);
},
};
TSではinterfaceを使った記述もあります。上記typeの記述と同じ内容になります。
interface ColorfulCircleService extends ColorfulCircle {
draw: () => void;
}
Array について
PHPが配列における型制御が少ないです。
function drawAllCircle(array $circles): void {
foreach($circles as $circle) {
if (!($circle instanceof Circle)) {
// $circleにCircle以外の可能性があるので、
// ここでCircle型であることを確認しないといけない。
throw new TypeError('');
}
// ここから安全に $circle を Circle として使うことをできる
}
}
const drawAllCircle = (circles :Circle[]): void => {
for (let circle of circles) {
// ここから安全に circle を Circle として使うことをできる
}
}
TSでUnion型と配列を組み合わせて使う例
次のように配列に複数型の値を格納することができます。
type MixedDay = Date | null | string | undefined;
const days: MixedDay[] = [null, new Date, "2022-01-05"];
補足資料
TypeScriptのPlayground
TSのclassを省いた理由
TSにもclassの記述がたくさんあります。ただ、個人として主にReact+TSを開発を進めているのと、Reactコンポーネントの主流がclassコンポーネントからfunctionalコンポーネントに変わることもあり、classを使う場面がほとんどなくなったので、classを省きました。
参考記事
https://qiita.com/Yamamoto0525/items/e790d6ecd62edf61d9e0
https://www.typescriptlang.org/cheatsheets/