Install any skill in seconds. Free to start, no credit card required.
Get Started Free →建立語意化的 TypeScript 型別別名(Semantic Type Aliases)來替代未經約束的原始型別(如 string、number)。 讓型別系統表達業務語意,提升可讀性與型別安全。 當使用者提及以下關鍵字或情境時觸發: - "語意化型別"、"型別別名"、"type alias" - "將 string/number 拆分為有意義的型別" - "timestamp 型別"、"日期型別定義" - "typescript-types-segment"、"seg-types" - "型別重構"、"強化型別安全" - "semantic type"、"segmented type"、"primitive obsession"
.claude/skills/bluelovers-typescript-types-segment/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-15 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 30% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 13% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 0% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 37% | 0% |
本技能指導如何使用語意化型別別名(Semantic Type Aliases)取代籠統的原始型別(string、number),讓 TypeScript 型別系統表達業務語意、提升可讀性與型別安全。
TypeScript 的原始型別(string、number)僅描述「值的形狀」,無法表達「值的用途」。
typescript// ❌ 模糊:無法區分這四個參數的業務差異 function processData( userId: string, timestamp: number, amount: number, status: string ): void {} // ✅ 清晰:型別名稱即文件 function processData( userId: IUserId, timestamp: ITimestampUnix, amount: ICurrencyAmount, status: IOrderStatus ): void {}
TypeScript 的 type 別名(Type Alias)在編譯後會被完全移除,不會產生runtime overhead,也不會有 nominal typing 的保護。
typescript// ITimestampUnix 在編譯後就是 number // 的好處是語意清晰 + 開發體驗佳 export type ITimestampUnix = number;
嚴格遵循專案既有的 TypeScript 命名慣例(參考 typescript-naming-convention 規則):
| 型別種類 | 命名格式 | 範例 | |---------|---------|------| | Type Alias | I{Prefix}{Name} | ITimestampUnix、IUserId | | 聯合型別 | I{Prefix}{Name} | ITimestampUnknown | | Template Literal | I{Prefix}{Name} | ITimestampMillisecondsString |
IUserId(positive integer)、IPercentage(0-100)搜尋程式碼中模糊的 string、number 使用:
typescript// 待重構的目標 const createdAt: string = ...; const updatedAt: number = ...;
為每個原始型別定義清晰的語意:
| 原始型別 | 語意分類 | 建議型別名稱 | |---------|---------|------------| | number (Unix timestamp, 10 digits) | 秒級時間戳 | ITimestampUnix | | number (milliseconds, 13 digits) | 毫秒時間戳 | ITimestampMilliseconds | | ${number} (13 digits string) | 毫秒時間戳字串 | ITimestampMillisecondsString | | string (UUID format) | 使用者識別碼 | IUserId | | string (ISO 8601 with timezone) | 含時區 ISO 日期 | IDateISO8601WithTz |
使用雙語區塊註解(繁體中文 + English)說明型別用途:
typescript/** * Unix 時間戳(10 位數,秒) * Unix timestamp (10 digits, seconds) * * @example * dayjs().unix() 返回的時間戳 */ export type ITimestampUnix = number; /** * 毫秒時間戳(13 位數) * Millisecond timestamp (13 digits) * * @example * Date.now() 返回的時間戳 * dayjs().valueOf() 返回的時間戳 */ export type ITimestampMilliseconds = number; /** * 毫秒時間戳字串(13 位數) * Millisecond timestamp string (13 digits) */ export type ITimestampMillisecondsString = `${number}`; /** * 時間戳型別(聯合:秒或毫秒) * Timestamp type (union: seconds or milliseconds) * * @deprecated 僅限尚未得知目標格式時使用 */ export type ITimestampUnknown = ITimestampUnix | ITimestampMilliseconds;
對於具有固定格式的字串,使用 Template Literal Types 進行編譯期約束:
typescript/** * ISO 8601 完整日期時間(含時區) * ISO 8601 full datetime with timezone * * @example 2024-01-15T10:30:00+08:00 */ export type IDateISO8601WithTz = `${IDateISO8601Date}T${IDateISO8601Time}${string}`; /** * ISO 8601 僅日期部分 * ISO 8601 date only * * @example 2024-01-15 */ export type IDateISO8601Date = `${number}-${number}-${number}`; /** * ISO 8601 僅時間部分 * ISO 8601 time only * * @example 10:30:00 */ export type IDateISO8601Time = `${number}:${number}:${number}`; /** * 包含任意日期的字串,需透過提取函式處理 * String containing any date, needs extraction function */ export type IStringIncludeAnyDate = string;
語意化型別應集中管理於專案的型別目錄中:
project/
├── lib/types/
│ ├── seg-types/ # 語意化型別的主要目錄
│ │ ├── seg.ts # 一般語意型別
│ │ ├── seg-timestamp.ts # 時間相關型別
│ │ └── seg-external.ts # 外部 API 型別對於來自外部 API 且類型不明的資料,建立語意化外部未知型別:
typescript/** * 外部 API 定義的未知類型資料,目前僅接收到回傳 null * External API unknown type, currently only receiving null */ export type ISegExternalUnknownNull = unknown | null; /** * 外部 API 定義的未知類型資料,目前沒有接收過資料 * External API unknown type, never received any data (not even null) */ export type ISegExternalUnknownNever = unknown; /** * 外部 API 定義的未知類型資料,但可能為數字類型 * External API unknown type, possibly numeric */ export type ISegExternalUnknownNumber = number | null; /** * 外部 API 定義的未知類型資料,但可能為字串類型 * External API unknown type, possibly string */ export type ISegExternalUnknownString = string | null; /** * 外部 API 定義的未知類型資料,但可能為 UUID 類型 * External API unknown type, possibly UUID */ export type ISegExternalUnknownUuid = string | null; /** * 外部 API 定義的未知類型資料,但可能為陣列類型 * External API unknown type, possibly array */ export type ISegExternalUnknownArray<T extends unknown = unknown> = T[] | null;
詳見:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | pass→pass | 11,995 | 6,614 | -45% | 1 | 1 | 0% | 1,792 | 3,143 | +75% | 0 | 0 | — |
case-15 | fail→pass | 6,682 | 2,393 | -64% | 1 | 1 | 0% | 1,336 | 2,379 | +78% | 0 | 0 | — |
case-01 | fail→pass | 18,356 | 14,493 | -21% | 1 | 1 | 0% | 3,379 | 4,400 | +30% | 0 | 0 | — |
case-02 | fail→pass | 20,277 | 12,759 | -37% | 1 | 1 | 0% | 3,963 | 4,471 | +13% | 0 | 0 | — |
case-03 | fail→pass | 20,029 | 10,604 | -47% | 1 | 1 | 0% | 4,388 | 4,380 | -0% | 0 | 0 | — |
case-05 | fail→pass | 10,655 | 3,693 | -65% | 1 | 1 | 0% | 1,979 | 2,708 | +37% | 0 | 0 | — |
case-06 | fail→fail | 8,388 | 9,462 | +13% | 1 | 1 | 0% | 1,817 | 4,058 | +123% | 0 | 0 | — |
case-07 | fail→fail | 12,404 | 6,828 | -45% | 1 | 1 | 0% | 2,900 | 3,626 | +25% | 0 | 0 | — |
case-08 | fail→pass | 23,285 | 4,240 | -82% | 1 | 1 | 0% | 4,393 | 2,811 | -36% | 0 | 0 | — |
case-09 | fail→pass | 7,047 | 7,740 | +10% | 1 | 1 | 0% | 1,408 | 3,276 | +133% | 0 | 0 | — |
case-10 | fail→fail | 11,697 | 4,347 | -63% | 1 | 1 | 0% | 2,100 | 2,715 | +29% | 0 | 0 | — |
case-11 | fail→pass | 8,905 | 4,052 | -54% | 1 | 1 | 0% | 1,464 | 2,608 | +78% | 0 | 0 | — |
case-12 | fail→fail | 7,500 | 4,786 | -36% | 1 | 1 | 0% | 1,481 | 2,868 | +94% | 0 | 0 | — |
case-13 | fail→pass | 8,000 | 6,213 | -22% | 1 | 1 | 0% | 1,552 | 3,285 | +112% | 0 | 0 | — |
case-14 | fail→pass | 13,911 | 13,145 | -6% | 1 | 1 | 0% | 2,390 | 4,249 | +78% | 0 | 0 | — |
case-16 | fail→pass | 8,872 | 2,828 | -68% | 1 | 1 | 0% | 1,520 | 2,529 | +66% | 0 | 0 | — |
case-17 | fail→fail | 11,152 | 5,299 | -52% | 1 | 1 | 0% | 1,872 | 2,689 | +44% | 0 | 0 | — |
case-18 | fail→pass | 9,502 | 5,260 | -45% | 1 | 1 | 0% | 1,606 | 2,897 | +80% | 0 | 0 | — |
case-19 | fail→fail | 10,328 | 5,370 | -48% | 1 | 1 | 0% | 1,846 | 2,859 | +55% | 0 | 0 | — |
case-20 | pass→pass | 10,649 | 9,156 | -14% | 1 | 1 | 0% | 2,298 | 4,033 | +76% | 0 | 0 | — |
case-21 | pass→pass | 2,761 | 2,495 | -10% | 1 | 1 | 0% | 513 | 2,388 | +365% | 0 | 0 | — |
case-22 | pass→pass | 10,455 | 9,590 | -8% | 1 | 1 | 0% | 2,390 | 3,913 | +64% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.