日頃コードを書いている時にあると便利なコード量を減らすためのTipsをまとめておこうかと思います。
見返すというよりは、体が覚えているものがどれくらいあるかを確認する、みたいな使い方で。
他の言語でも似たような概念はあると思いますが、ここではTypeScriptで。
null判定関連演算子
おそらく最も頻度が高い、null判定の演算子です。
// ==========================================
// Optional Chaining (?.)
// ==========================================
// ❌ 従来の書き方
function getUserCity(user: any) {
if (user && user.address && user.address.city) {
return user.address.city;
}
return undefined;
}
// ✅ Optional Chainingを使った書き方
function getUserCityModern(user: any) {
return user?.address?.city;
}
// 配列やメソッドにも使える
const users = [{ name: 'Alice' }, { name: 'Bob' }];
const firstUserName = users?.[0]?.name; // 'Alice'
const thirdUserName = users?.[2]?.name; // undefined
// メソッド呼び出し
const result = obj?.method?.(); // methodが存在すれば呼び出す
// ==========================================
// Nullish Coalescing (??)
// ==========================================
// ||は
// null
// NaN
// 0
// 空文字列 ("" または '' または ``)
// undefined
//
// ??はnull/undefined
//
// ❌ 従来の書き方(falsy値を全て除外してしまう)
function getPort(config: any) {
return config.port || 3000; // port が 0 の場合も 3000 になってしまう
}
// ✅ Nullish Coalescingを使った書き方
function getPortModern(config: any) {
return config.port ?? 3000; // null/undefined のみデフォルト値を使用
}
// 実用例
const port = 0;
console.log(port || 8080); // 8080 (意図しない動作)
console.log(port ?? 8080); // 0 (正しい動作)
const name = '';
console.log(name || 'Anonymous'); // 'Anonymous' (空文字もfalsyと判定)
console.log(name ?? 'Anonymous'); // '' (null/undefinedではないのでそのまま)
// ==========================================
// Nullish Coalescing Assignment (??=)
// ==========================================
// 値が null/undefined の場合のみ代入
let config: any = {};
// ❌ 従来の書き方
if (config.timeout === null || config.timeout === undefined) {
config.timeout = 5000;
}
// ✅ Nullish Coalescing Assignmentを使った書き方
config.timeout ??= 5000;
// 実用例
const settings: any = { theme: 'dark' };
settings.timeout ??= 3000; // timeout が未設定なので 3000 が設定される
settings.theme ??= 'light'; // theme は既に設定済みなので 'dark' のまま
// ==========================================
// 三項演算子のネスト
// ==========================================
// ❌ 従来の書き方(9行)
function getStatusColor(status: Status): string {
if (status === 'success') {
return 'green';
} else if (status === 'warning') {
return 'yellow';
} else if (status === 'error') {
return 'red';
} else {
return 'gray';
}
}
// ✅ 三項演算子(1行)
const getStatusColorModern = (status: Status): string =>
status === 'success' ? 'green' :
status === 'warning' ? 'yellow' :
status === 'error' ? 'red' : 'gray';
// ==========================================
// 実践的な組み合わせ例
// ==========================================
interface User {
name: string;
profile?: {
bio?: string;
social?: {
twitter?: string;
github?: string;
};
};
preferences?: {
theme?: string;
notifications?: boolean;
};
}
class UserService {
// APIレスポンスの安全な処理
getUserDisplayName(user: User | null | undefined): string {
return user?.name ?? 'Guest User';
}
// ネストしたプロパティへの安全なアクセス
getTwitterHandle(user: User): string {
return user?.profile?.social?.twitter ?? 'N/A';
}
// デフォルト設定の適用
getUserTheme(user: User): string {
const theme = user?.preferences?.theme;
return theme ?? 'system'; // null/undefinedの場合のみデフォルト値
}
// 設定の初期化
initializeUserPreferences(user: User): void {
user.preferences ??= {}; // preferencesが未定義の場合のみ初期化
user.preferences.theme ??= 'light';
user.preferences.notifications ??= true;
}
}
メソッドチェーン
null演算子関連と並んで、頻度が高いです。
filter,forEach,mapなど。
lodashなどのライブラリを使うことでさらに拡張できます。
// ==========================================
// 配列メソッドチェーン
// ==========================================
interface UserData {
name: string;
age: number;
}
// ❌ 従来の書き方(12行)
function processUsers(users: UserData[]): string[] {
const result: string[] = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 20) {
const name = users[i].name.toUpperCase();
result.push(name);
}
}
result.sort();
return result;
}
// ✅ メソッドチェーン(4行)
const processUsersModern = (users: UserData[]): string[] =>
users
.filter(u => u.age >= 20)
.map(u => u.name.toUpperCase())
.sort();
配列の論理判定
使いすぎて、if文がわかりにくくならないように注意。
// ==========================================
// 配列の便利メソッド
// ==========================================
const numbers2: number[] = [1, 2, 3, 4, 5];
// すべてが条件を満たすか
const allPositive: boolean = numbers2.every(n => n > 0); // true
// 少なくとも1つが条件を満たすか
const hasEven: boolean = numbers2.some(n => n % 2 === 0); // true
// 条件を満たす最初の要素
const firstEven: number | undefined = numbers2.find(n => n % 2 === 0); // 2
// 条件を満たす要素のインデックス
const evenIndex: number = numbers2.findIndex(n => n % 2 === 0); // 1
アロー演算子の省略記法
そのままでもコード量の節約になっていますが、できるとスマートかも。
// ==========================================
// アロー関数の省略記法
// ==========================================
// ❌ 従来の書き方
const double = function(x: number): number {
return x * 2;
};
// ✅ アロー関数(暗黙のreturn)
const doubleModern = (x: number): number => x * 2;
// オブジェクトを返す場合
const createUser = (name: string, age: number): { name: string; age: number } =>
({ name, age });
// 配列メソッドと組み合わせ
const numbers: number[] = [1, 2, 3, 4, 5];
const doubled: number[] = numbers.map(x => x * 2);
const evens: number[] = numbers.filter(x => x % 2 === 0);
const sum: number = numbers.reduce((acc, x) => acc + x, 0);
分割代入、スプレッド構文
オブジェクトの展開や結合系の処理など。
// ==========================================
// 型定義
// ==========================================
interface User {
name: string;
age: number;
email: string;
address: {
city: string;
country?: string;
};
profile?: {
avatar?: string;
bio?: string;
};
}
interface Settings {
theme?: string;
language?: string;
notifications?: boolean;
}
interface Config {
timeout?: number;
port?: number;
host?: string;
}
type StatusCode = 200 | 404 | 500;
type Status = 'success' | 'warning' | 'error' | 'pending';
// ==========================================
// 分割代入 (Destructuring)
// ==========================================
// ❌ 従来の書き方(7行)
function displayUser(user: User): string {
const name = user.name;
const age = user.age;
const email = user.email;
const city = user.address.city;
return `${name}, ${age}, ${email}, ${city}`;
}
// ✅ 分割代入(2行)
function displayUserModern(user: User): string {
const { name, age, email, address: { city } } = user;
return `${name}, ${age}, ${email}, ${city}`;
}
// デフォルト値も設定可能
const getSettings = (settings: Settings) => {
const { theme = 'light', language = 'en' } = settings;
return { theme, language };
};
// 配列の分割代入
const [first, second, ...rest]: number[] = [1, 2, 3, 4, 5];
スプレッド構文
人のコードでこれがあってわからない時がたまにあるので要注意。
// ==========================================
// スプレッド構文 (Spread Syntax)
// ==========================================
// ❌ 従来の書き方
const obj1: Record<string, number> = { a: 1, b: 2 };
const obj2: Record<string, number> = { c: 3 };
const merged = Object.assign({}, obj1, obj2);
// ✅ スプレッド構文
const merged2 = { ...obj1, ...obj2 };
// 配列の結合
const arr1: number[] = [1, 2];
const arr2: number[] = [3, 4];
const combined: number[] = [...arr1, ...arr2]; // [1, 2, 3, 4]
// 関数引数として展開
const numbers: number[] = [1, 5, 3, 9, 2];
console.log(Math.max(...numbers)); // 9
// オブジェクトの部分更新
const user: User = {
name: 'Alice',
age: 25,
email: 'alice@example.com',
address: { city: 'Tokyo' }
};
const updated: User = { ...user, age: 26 }; // ageだけ更新
プロパティ名の省略
たくさんある時にこれがあると非常にありがたいです。
// ==========================================
// 10. プロパティ名の省略記法
// ==========================================
// ❌ 従来の書き方
const createUserOld = (name: string, age: number) => {
return {
name: name,
age: age,
greet: function(): string {
return 'Hello';
}
};
};
// ✅ 省略記法
const createUserModern2 = (name: string, age: number) => ({
name,
age,
greet(): string {
return 'Hello';
}
});