0
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コード量削減Tips

Posted at

日頃コードを書いている時にあると便利なコード量を減らすための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';
  }
});
0
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?