Typescriptでファイルシステムのフォルダ構造を型で表現する
※ 当ページには【広告/PR】を含む場合があります。
2025/07/15

フォルダ構造について
tree
tree
.
├── dir1
│ ├── fileA.txt
│ └── dir2
│ └── fileB.txt
├── fileC.txt
└── dir3
└── fileD.txt
3 directories, 4 files
type FileOrFolder = {
__type: 'file' | 'folder',
createdAt: Date,
updatedAt: Date,
}
フォルダ構造の型を設計する
FileOrFolder[]
//ファイル・フォルダが共通してもつプロパティ
type FileOrEmptyFolder = {
__type: 'file' | 'folder',
createdAt: Date,
updatedAt: Date,
};
//(空でない)フォルダを表現した構造
type Folder = FileOrEmptyFolder & {
[K in Exclude<string, keyof FileOrEmptyFolder>]: FileOrEmptyFolder | Folder
};
//フォルダ構造
type FileOrFolder = FileOrEmptyFolder | Folder;
//フォルダ構造をテスト ... フォルダを表したFileOrFolder型のインスタンスの例
const fof: FileOrFolder = {
__type: 'folder',
createdAt: new Date(),
updatedAt: new Date(),
root: {
__type: 'folder',
createdAt: new Date(),
updatedAt: new Date(),
'a': {
__type: 'folder',
createdAt: new Date(),
updatedAt: new Date(),
'0': {
__type: 'file',
createdAt: new Date(),
updatedAt: new Date(),
}
},
'b.txt': {
__type: 'file',
createdAt: new Date(),
updatedAt: new Date(),
},
c: {
__type: 'folder',
createdAt: new Date(),
updatedAt: new Date(),
"e.txt": {
__type: 'file',
createdAt: new Date(),
updatedAt: new Date(),
}
}
}
};
FileOrEmptyFolder
__type
'file'
'folder'
createdAt
updatedAt
Folder
FileOrEmptyFolder
[K in Exclude<string, keyof FileOrEmptyFolder>]: FileOrEmptyFolder | Folder
FileOrEmptyFolder
__type
createdAt
updatedAt
K
FileOrEmptyFolder
Folder
FileOrFolder
FileOrEmptyFolder
Folder
fof
root
a
b.txt
c
a
0
c
e.txt
ユーティリティメソッドの実装例
function listDirOrFile(input: FileOrFolder | undefined, parent: string[] = ['']) {
if (input == null) return;
let result: FileOrFolder | null = null;
for (const key in input) {
if (['__type', 'createdAt', 'updatedAt'].includes(key)) {
continue;
}
console.log('PATH:', [...parent, key].join("/"));
const _child = (input as Folder)[key];
if (_child) {
listDirOrFile(_child, [...parent, key]);
}
}
return result;
}
listDirOrFile(fof);
"PATH:", "/root"
"PATH:", "/root/a"
"PATH:", "/root/a/0"
"PATH:", "/root/b.txt"
"PATH:", "/root/c"
"PATH:", "/root/c/e.txt"
function findDirOrFile(path: string, input: FileOrFolder): FileOrFolder | null {
if (input == null) return null;
let result: FileOrFolder = input;
for (const _key of path.split('/').splice(1)) {
if (_key === '') {
return input;
}
result = (result as Folder)[_key];
if (result == null) return null;
}
return result;
}
console.log('FOUND:', findDirOrFile('/root/c', fof));
console.log('FOUND:', findDirOrFile('/f', fof));
"FOUND:", {
"__type": "folder",
"createdAt": "2025-07-18T17:01:16.949Z",
"updatedAt": "2025-07-18T17:01:16.949Z",
"e.txt": {
"__type": "file",
"createdAt": "2025-07-18T17:01:16.949Z",
"updatedAt": "2025-07-18T17:01:16.949Z"
}
}
"FOUND:", null
/root/c
/f
まとめ
* **回帰的な型定義**:
フォルダが子要素として自身(フォルダ)やファイルを持つ構造を表現するために、
型自身を再帰的に参照する定義を使う
* **Mapped型の活用**:
フォルダ内の動的なファイル名やフォルダ名をプロパティとして表現するために、
Mapped型を利用している
* **実践的な型プログラミング**:
抽象的なファイルシステムを、Typescriptの型システムで具体的な型に落とし込んで表現できる
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー