カテゴリー
個人的Typescriptの型入門① 〜 型の基本
※ 当ページには【広告/PR】を含む場合があります。
2024/09/08
目次
- 1. 型の基礎
- 1-1. プリミティブ型
- 1-2. strictNullChecksオプション
- 1-3. リテラル型
- 1-4. リテラル型 --> 上位の型
- 1-5. リテラル型の型推論
- 1-6. let変数の型推論
- 1-7. オブジェクト型
- 1-8. オブジェクト型の型エラー
- 1-9. オブジェクト型の部分型
- 1-10. オブジェクトリテラル
- 1-11. オブジェクトリテラルで部分型を使う注意点
- 1-12. 配列型
- 1-13. 関数型
- 1-14. 関数型の引数と部分型
- 1-15. 関数型の引数の数と部分型
- 1-16. 可変長引数
- 1-17. void型
- 1-18. any型
- 1-19. クラス宣言とオブジェクト型
- 1-20. extends修飾子
- 1-21. ジェネリクス
- 1-22. オブジェクト型のジェネリクス
- 1-23. クラス定義や関数定義でもジェネリクス
- 1-24. ジェネリクス付きの関数型
- 1-25. 型引数の推論
- 1-26. ジェネリック付きの関数型のプロパティ限定
- 1-27. タプル型
- 1-28. タプル型の注意点
- 1-29. 要素数ゼロのタプル型
- 1-30. 可変長タプル型・その1
- 1-31. 可変長タプル型・その2
- 1-32. オプショナルな要素を持つタプル型
- 1-33. タプル型と関数の可変長引数
- 1-34. オプショナルな要素を持つタプル型を用いたオプショナルな引数を持つ関数型
- 1-35. 関数呼び出しのスプレッド構文とタプル型
- 1-36. タプル型と可変長引数とジェネリクス
- 1-37. Union型
- 1-38. オブジェクト型のUnion型
- 1-39. in演算子を使ったUnion型の絞り込み
- 1-40. in演算子を用いた型の絞り込みの注意点
- 1-41. typeof演算子を用いた型の絞り込み
- 1-42. nullチェック
- 1-43. &&や||でnullチェック
- 1-44. タグ付きUnion型でオブジェクト型の絞り込み
- 1-45. Union型のプロパティ
- 1-46. never型
- 1-47. never型のユースケース・その1
- 1-48. never型のユースケース・その2
- 1-49. Intersection型
- 1-50. Union型とIntersection型を組みあわせ
- 1-51. Union型を持つ関数型
- 1-52. 構成が全部関数型のUnion型
- 1-53. 引数がIntersection型で表現される関数型のUnion型
- 1-54. Union型にかかる機能制限
- 1-55. Object型
- 1-56. {}型
- 1-57. weak typeルール
- 1-58. unknown型
- 1-59. unknown型とany型の違い
- 1-60. unknown型の絞り込み
- 1-61. unknown型とvoid型の意外な関係
- 2. まとめ
型の基礎
プリミティブ型
string, number, boolean, symbol, bigint, null, undefined
const a: number = 3;
//✘ Type 'number' is not assignable to type 'string'.
const b: string = a;
strictNullChecksオプション
--strictNullChecks
strictNullChecks
const a: null = null;
//(strictNullChecksがオンの場合)
//✘ Type 'null' is not assignable to type 'string'.
const b: string = a;
リテラル型
const a: 'foo' = 'foo';
//✘ Type '"foo"' is not assignable to type '"bar"'.
const b: 'bar' = 'foo';
リテラル型 --> 上位の型
const a: 'foo' = 'foo';
const b: string = a;
const a: 3 = 3;
const b: number = a;
const a: true = true;
const b: boolean = a;
リテラル型の型推論
//aは'foo'型(文字列リテラル型)と推論
const a = 'foo';
//✘ Type '"foo"' is not assignable to type '"bar"'.
const b: 'bar' = a;
let変数の型推論
const
let/var
//aはstring型と推論
let a = 'foo';
const b: string = a;
//✘ Type 'string' is not assignable to type '"foo"'.
const c: 'foo' = a;
//aは'foo'型
let a: 'foo' = 'foo';
//✘Type '"bar"' is not assignable to type '"foo"'.
a = 'bar';
オブジェクト型
{ }
{foo: string; bar: number}
interface
type(型エイリアス)
interface MyObj {
foo: string;
bar: number;
}
type MyObj = {
foo: string;
bar: number;
}
{foo: string; bar: number}
interface MyObj {
foo: string;
bar: number;
}
const a: MyObj = {
foo: 'foo',
bar: 3,
};
{foo: string; bar: number}
オブジェクト型の型エラー
interface MyObj {
foo: string;
bar: number;
}
const a: MyObj = {
foo: 'foo',
//✘Type 'string' is not assignable to type 'number'.
bar: 'BARBARBAR',
};
//✘Property 'bar' is missing in type '{ foo: string; }' but required in type 'MyObj'.
const b: MyObj = {
foo: 'foo',
};
オブジェクト型の部分型
interface MyObj {
foo: string;
bar: number;
}
interface MyObj2 {
foo: string;
}
const a: MyObj = {foo: 'foo', bar: 3};
const b: MyObj2 = a;
オブジェクトリテラル
interface
type
{...}
{foo: string, bar: numbder}
「オブジェクトリテラル」
let obj1: {id: number, name: string};
const obj2 = {id: 1, name: "hoge"};
{id: number, name: string}
{id: number, name: string}
{id: 1, name: "hoge"}
オブジェクトリテラルで部分型を使う注意点
interface MyObj {
foo: string;
bar: number;
}
interface MyObj2 {
foo: string;
}
//✗Object literal may only specify known properties, and 'bar' does not exist in type 'MyObj2'.
const b: MyObj2 = {foo: 'foo', bar: 3};
//あまり意味は無いが以下ならエラーなし
const b: MyObj2 = {foo: 'foo', bar: 3} as MyObj;
{foo: 'foo', bar: 3}
{foo: string, bar: number}
interface MyObj2 {
foo: string;
}
function func(obj: MyObj2): void { }
//✗Object literal may only specify known properties, and 'bar' does not exist in type 'MyObj2'.
func({foo: 'foo', bar: 3});
//あまり意味は無いが以下ならエラーなし
func({foo: 'foo', bar: 3} as MyObj2);
配列型
[]
Array<>
const foo: number[] = [0, 1, 2, 3];
//もしくは
//const foo: Array<number> = [0, 1, 2, 3];
foo.push(4);
関数型
(x: string, y: number)=>boolean
function func(arg: string): number {
return Number(arg);
}
const f: (foo: string)=>number = func;
関数型の引数と部分型
(obj: MyObj2)=>void
(obj: MyObj)=>void
interface MyObj {
foo: string;
bar: number;
}
interface MyObj2 {
foo: string;
}
const a: (obj: MyObj2)=>void = () => {};
const b: (obj: MyObj)=>void = a;
interface MyObj {
foo: string;
bar: number;
}
interface MyObj2 {
foo: string;
}
const b: (obj: MyObj)=>void = () => {};
//✗ Type '(obj: MyObj) => void' is not assignable to type '(obj: MyObj2) => void'.
// Types of parameters 'obj' and 'obj' are incompatible.
// Property 'bar' is missing in type 'MyObj2' but required in type 'MyObj'.
const a: (obj: MyObj2)=>void = b;
関数型の引数の数と部分型
(foo: string)=>void
(foo: string, bar: number)=>void
const f1: (foo: string)=>void = () => {};
const f2: (foo: string, bar: number)=>void = f1;
bar: number
const f1: (foo: string, bar: number)=>void = ()=>{};
//✗ Type '(foo: string, bar: number) => void' is not assignable to type '(foo: string) => void'.
// Target signature provides too few arguments. Expected 2 or more, but got 1.
const f2: (foo: string)=>void = f1;
const f1: (foo: string)=>void = () => {};
//✗ Expected 1 arguments, but got 2.
f1('foo', 3);
可変長引数
number[]
const func = (foo: string, ...bar: number[]) => bar;
func('foo');
func('bar', 1, 2, 3);
//✗ Argument of type '"hey"' is not assignable to parameter of type 'number'.
func('baz', 'hey', 2, 3);
void型
const a: void = undefined;
const a: void = undefined;
//✗ Type 'void' is not assignable to type 'undefined'.
const b: undefined = a;
function foo(): void {
console.log('hello');
}
any型
const a: any = 3;
const b: string = a;
クラス宣言とオブジェクト型
class
class
class Foo {
method(): void {
console.log('Hello, world!');
}
}
const obj: Foo = new Foo();
const obj2: Foo = {
method: () => {}
};
class
class
interface
type
interface MyFoo {
method: () => void;
}
class Foo {
method(): void {
console.log('Hello, world!');
}
}
const obj: MyFoo = new Foo();
const obj2: Foo = obj;
extends修飾子
「extends」
interface User {
name: string;
}
interface Root extends User {
isMaster: boolean;
}
//👇Rootは以下と同じ定義
// interface Root {
// name: string;
// isMaster: boolean;
// }
ジェネリクス
type A<T> = T;
const str: A<string> = 'moji';
const num: A<number> = 123;
オブジェクト型のジェネリクス
< >
S
T
Foo
Foo<S,T>
interface Foo<S, T> {
foo: S;
bar: T;
}
const obj: Foo<number, string> = {
foo: 3,
bar: 'hi',
};
Foo<number, string>
number
string
クラス定義や関数定義でもジェネリクス
< >
class Foo<T> {
constructor(obj: T) {}
}
function func<T>(obj: T): void { }
const obj = new Foo<string>('foo');
func<number>(3);
ジェネリクス付きの関数型
<T>(obj: T)=>void
function func<T>(obj: T): void {}
const f: <T>(obj: T)=>void = func;
型引数の推論
func<number>(3)
<number>
func(3)
getId(3)
function getId<T>(value: T): T {
return value;
}
const value = getId(3);
const num: number = value;
const three: 3 = value;
//✘Type '3' is not assignable to type 'string'.
const str: string = value;
3
3
3
ジェネリック付きの関数型のプロパティ限定
function f<T>(arg: T): string {
//✗ Property 'name' does not exist on type 'T'.
return arg.name;
}
extends
interface User {
name: string;
age: number;
};
function f<T extends User>(arg: T): string {
return arg.name;
}
f({ name: 'aaa', age: 123 });
//✗ Argument of type '{ age: number; }' is not assignable to parameter of type 'User'.
// Property 'name' is missing in type '{ age: number; }' but required in type 'User'.
f({ age: 123 });
タプル型
const foo: [string, number] = ['foo', 5];
const str: string = foo[0];
function makePair(x: string, y: number): [string, number] {
return [x, y];
}
[string, number]
タプル型の注意点
const tuple: [string, number] = ['foo', 3];
tuple.pop();
tuple.push('Hey!');
const num: number = tuple[1];
//"Hey!"
console.log(num);
要素数ゼロのタプル型
any[]
unknown[]
T[]
const unit: [] = [];
可変長タプル型・その1
type NumAndStrings = [number, ...string[]];
const a1: NumAndStrings = [3, 'foo', 'bar'];
const a2: NumAndStrings = [5];
//✘ Type 'string' is not assignable to type 'number'.
const a3: NumAndStrings = ['foo', 'bar'];
可変長タプル型・その2
type StrsAndNumber = [...string[], number];
const b1: StrsAndNumber = ['foo', 'bar', 'baz', 0];
const b2: StrsAndNumber = [123];
// ✘ Type '[string, string]' is not assignable to type 'StrsAndNumber'.
// Type at position 1 in source is not compatible with type at position 1 in target.
// Type 'string' is not assignable to type 'number'.
const b3: StrsAndNumber = ['foo', 'bar'];
//✗ A rest element cannot follow another rest element.
type StrsAndNumber = [...string[], ...number[]];
オプショナルな要素を持つタプル型
[string, number?]
type T = [string, number?];
const t1: T = ['foo'];
const t2: T = ['foo', 3];
//✗ A required element cannot follow an optional element.
type T = [string?, number];
タプル型と関数の可変長引数
Args(=[string, number, boolean])
type Args = [string, number, boolean];
const func = (...args: Args) => args[1];
//vはnumber型
const v = func('foo', 3, true);
オプショナルな要素を持つタプル型を用いたオプショナルな引数を持つ関数型
type Args = [string, ...number[]];
const func = (f: string, ...args: Args) => args[0];
const v1 = func('foo', 'bar');
const v2 = func('foo', 'bar', 1, 2, 3);
関数呼び出しのスプレッド構文とタプル型
const func = (...args: string[]) => args[0];
const strings: string[] = ['foo', 'bar', 'baz'];
func(...strings);
const func = (str: string, num: number, b: boolean) => args[0] + args[1];
const args: [string, number, boolean] = ['foo', 3, false];
console.log(func(...args));
タプル型と可変長引数とジェネリクス
function bind<T, U extends any[], R>(
func: (arg1: T, ...rest: U)=>R,
value: T,
): ((...args: U) => R) {
return (...args: U) => func(value, ...args);
}
const add = (x: number, y: number) => x + y;
const add1 = bind(add, 1);
console.log(add1(5)); // 6
//✗ Argument of type '"foo"' is not assignable to parameter of type 'number'.
add1('foo');
U extends any[]
...rest: U
bind(add, 1)
T = number, U = [number], R = number
(...args: U)=>R
(arg: number)=>number
Union型
「|」
string | number
let value: string | number = 'foo';
value = 100;
value = 'bar';
//✘ Type 'true' is not assignable to type 'string | number'.
value = true;
オブジェクト型のUnion型
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: number;
baz: boolean;
}
type HogePiyo = Hoge | Piyo;
const obj: HogePiyo = {
foo: 'hello',
bar: 0,
};
in演算子を使ったUnion型の絞り込み
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: number;
baz: boolean;
}
function useHogePiyo(obj: Hoge | Piyo): void {
// objはHoge | Piyo型
if ('bar' in obj) {
//barプロパティがあるならHoge型
console.log('Hoge', obj.bar);
} else {
//barプロパティがないならPiyo型
console.log('Piyo', obj.baz);
}
}
'bar' in obj
in演算子を用いた型の絞り込みの注意点
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: number;
baz: boolean;
}
function useHogePiyo(obj: Hoge | Piyo): void {
//objはHoge | Piyo型
if ('bar' in obj) {
//barプロパティがある --> objはHoge型
console.log('Hoge', obj.bar);
} else {
//barプロパティがない --> objはPiyo型
console.log('Piyo', obj.baz);
}
}
const obj: Hoge | Piyo = {
foo: 123,
bar: 'bar',
baz: true,
};
useHogePiyo(obj);
bar: string
Hoge | Piyo
'bar' in obj
typeof演算子を用いた型の絞り込み
typeof <変数>
let foo = 'str';
//FooTypeはstring
type FooType = typeof foo;
const str: FooType = 'abcdef';
function func(value: string | number): number {
if ('string' === typeof value) {
// valueはstring型なのでlengthプロパティを見ることができる
return value.length;
} else {
// valueはnumber型
return value;
}
}
nullチェック
string | null
value != null
function func(value: string | null): number {
if (value != null) {
//valueはstring型に絞り込まれる
return value.length;
} else {
return 0;
}
}
&&や||でnullチェック
&&
||
function func(value: string | null): number {
return value != null && value.length || 0;
}
タグ付きUnion型でオブジェクト型の絞り込み
interface Some<T> {
type: 'Some';
value: T;
}
interface None {
type: 'None';
}
type Option<T> = Some<T> | None;
function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
if (obj.type === 'Some') {
//objはSome<T>型
return {
type: 'Some',
value: f(obj.value),
};
} else {
//objはNone型
return {
type: 'None',
};
}
}
Option<T>
Some<T>
None
typeプロパティ
'Some'/'None'
switch
interface Some<T> {
type: 'Some';
value: T;
}
interface None {
type: 'None';
}
type Option<T> = Some<T> | None;
function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
switch (obj.type) {
case 'Some':
return {
type: 'Some',
value: f(obj.value),
};
case 'None':
return {
type: 'None',
};
}
}
Union型のプロパティ
Hoge | Piyo
string | number
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: number;
baz: boolean;
}
type HogePiyo = Hoge | Piyo;
function getFoo(obj: HogePiyo): string | number {
//obj.fooはstring | number型
return obj.foo;
}
const arr: string[] | number[] = [];
//string[] | number[]型の配列の要素はstring | number型
const elm = arr[0];
never型
//✘ Type '0' is not assignable to type 'never'.
const n: never = 0;
//never型の値を作る方法が無いのでdeclareで無理やり宣言
declare const n: never;
const foo: string = n;
never型のユースケース・その1
interface Some<T> {
type: 'Some';
value: T;
}
interface None {
type: 'None';
}
type Option<T> = Some<T> | None;
function map<T, U>(obj: Option<T>, f: (obj: T)=> U): Option<U> {
switch (obj.type) {
case 'Some':
return {
type: 'Some',
value: f(obj.value),
};
case 'None':
return {
type: 'None',
};
default:
//objはnever型
return obj;
}
}
never型のユースケース・その2
function func(): never {
throw new Error('Hi');
}
const result: never = func();
Intersection型
「&」
Hoge & Piyo
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: string;
baz: boolean;
}
const obj: Hoge & Piyo = {
foo: 'foo',
bar: 3,
baz: true,
};
Union型とIntersection型を組みあわせ
(Hoge | Piyo) & Fuga
(Hoge & Fuga) | (Piyo & Fuga)
interface Hoge {
type: 'hoge';
foo: string;
}
interface Piyo {
type: 'piyo';
bar: number;
}
interface Fuga {
baz: boolean;
}
type Obj = (Hoge | Piyo) & Fuga;
function func(obj: Obj) {
//objはFuga型でもありbazを参照可
console.log(obj.baz);
if (obj.type === 'hoge') {
//objはHoge & Fuga型
console.log(obj.foo);
} else {
// objはPiyo & Fuga型
console.log(obj.bar);
}
}
Union型を持つ関数型
type Func = (arg: number) => number;
interface MyObj {
prop: string;
}
const obj : Func | MyObj = { prop: '' };
//✘ Cannot invoke an expression whose type lacks a call signature.
// Type 'MyObj' has no compatible call signatures.
obj(123);
構成が全部関数型のUnion型
type StrFunc = (arg: string) => string;
type NumFunc = (arg: number) => string;
declare const obj : StrFunc | NumFunc;
//✘ Argument of type '123' is not assignable to parameter of type 'string & number'.
// Type '123' is not assignable to type 'string'.
obj(123);
StrFunc | NumFunc
StrFunc | NumFunc
string & number
string & number
string & number
引数がIntersection型で表現される関数型のUnion型
HogeFunc | PiyoFunc
interface Hoge {
foo: string;
bar: number;
}
interface Piyo {
foo: string;
baz: boolean;
}
type HogeFunc = (arg: Hoge) => number;
type PiyoFunc = (arg: Piyo) => boolean;
declare const func: HogeFunc | PiyoFunc;
//resは number | boolean 型
const res = func({
foo: 'foo',
bar: 123,
baz: false,
});
HogeFunc | PiyoFunc
Hoge & Piyo
Union型にかかる機能制限
const arr: string[] | number[] = [];
//✘ Parameter 'x' implicitly has an 'any' type.
arr.forEach(x => console.log(x));
//✘ Cannot invoke an expression whose type lacks a call signature.
const arr2 = arr.map(x => x);
Object型
object
//✘ Argument of type '3' is not assignable to parameter of type 'object | null'.
Object.create(3);
{}型
{}
{foo: string}
{}
const obj = { foo: 'foo' };
const obj2: {} = obj;
{}
const o: {} = 3;
length: number;
interface Length {
length: number;
}
const o: Length = 'foobar';
weak typeルール
{}
{}
interface Options {
foo?: string;
bar?: number;
}
const obj1 = { hoge: 3 };
//✘ Type '{ hoge: number; }' has no properties in common with type 'Options'
const obj2: Options = obj1;
//✘ Type '5' has no properties in common with type 'Options'.
const obj3: Options = 5;
{}
{ hoge: number; }
{}
{}
{}
unknown型
{}
const u1: unknown = 3;
const u2: unknown = null;
const u3: unknown = (foo: string)=> true;
unknown型とany型の違い
const u: unknown = 3;
//✘ Object is of type 'unknown'.
const sum = u + 5;
//✘ Object is of type 'unknown'.
const p = u.prop;
unknown型の絞り込み
const u: unknown = 3;
if (typeof u === 'number') {
// この中ではuはnumber型
const foo = u + 5;
}
instanceof
const u: unknown = 3;
class MyClass {
public prop: number = 10;
}
if (u instanceof MyClass) {
//uはMyClass型
u.prop;
}
unknown型とvoid型の意外な関係
const func: ()=>number = () => 123;
const f: ()=>void = func;
()=>number
()=>void
const v: void = 1;
const func: ()=>number = () => 123;
const f: ()=>void = func;
const value: void = f();
//123
console.log(value);
まとめ
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー
- 1. 型の基礎
- 1-1. プリミティブ型
- 1-2. strictNullChecksオプション
- 1-3. リテラル型
- 1-4. リテラル型 --> 上位の型
- 1-5. リテラル型の型推論
- 1-6. let変数の型推論
- 1-7. オブジェクト型
- 1-8. オブジェクト型の型エラー
- 1-9. オブジェクト型の部分型
- 1-10. オブジェクトリテラル
- 1-11. オブジェクトリテラルで部分型を使う注意点
- 1-12. 配列型
- 1-13. 関数型
- 1-14. 関数型の引数と部分型
- 1-15. 関数型の引数の数と部分型
- 1-16. 可変長引数
- 1-17. void型
- 1-18. any型
- 1-19. クラス宣言とオブジェクト型
- 1-20. extends修飾子
- 1-21. ジェネリクス
- 1-22. オブジェクト型のジェネリクス
- 1-23. クラス定義や関数定義でもジェネリクス
- 1-24. ジェネリクス付きの関数型
- 1-25. 型引数の推論
- 1-26. ジェネリック付きの関数型のプロパティ限定
- 1-27. タプル型
- 1-28. タプル型の注意点
- 1-29. 要素数ゼロのタプル型
- 1-30. 可変長タプル型・その1
- 1-31. 可変長タプル型・その2
- 1-32. オプショナルな要素を持つタプル型
- 1-33. タプル型と関数の可変長引数
- 1-34. オプショナルな要素を持つタプル型を用いたオプショナルな引数を持つ関数型
- 1-35. 関数呼び出しのスプレッド構文とタプル型
- 1-36. タプル型と可変長引数とジェネリクス
- 1-37. Union型
- 1-38. オブジェクト型のUnion型
- 1-39. in演算子を使ったUnion型の絞り込み
- 1-40. in演算子を用いた型の絞り込みの注意点
- 1-41. typeof演算子を用いた型の絞り込み
- 1-42. nullチェック
- 1-43. &&や||でnullチェック
- 1-44. タグ付きUnion型でオブジェクト型の絞り込み
- 1-45. Union型のプロパティ
- 1-46. never型
- 1-47. never型のユースケース・その1
- 1-48. never型のユースケース・その2
- 1-49. Intersection型
- 1-50. Union型とIntersection型を組みあわせ
- 1-51. Union型を持つ関数型
- 1-52. 構成が全部関数型のUnion型
- 1-53. 引数がIntersection型で表現される関数型のUnion型
- 1-54. Union型にかかる機能制限
- 1-55. Object型
- 1-56. {}型
- 1-57. weak typeルール
- 1-58. unknown型
- 1-59. unknown型とany型の違い
- 1-60. unknown型の絞り込み
- 1-61. unknown型とvoid型の意外な関係
- 2. まとめ