Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

dry-types干型

Agent Skill

dry-types 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

214

周安装

9

GitHub Stars

2

下载量

75
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dry-types(干型)
来源仓库:https://github.com/marius-townhouse/effective-typescript-skills
仓库路径:skills/dry-types
安装命令:
npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill dry-types
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill dry-types

简介

该技能将 DRY 原则应用于 TypeScript 类型系统,避免类型重复定义。

  • 适用于接口字段复用、类型子集创建和部分类型派生。
  • 使用 TypeScript 类型操作符从现有类型派生新类型,保持同步更新。
  • 安装需从 GitHub 仓库获取,使用前应确认 TypeScript 项目配置正确。
  • 涉及复杂类型关系时,应测试类型推导结果,确保编译时类型安全。

SKILL.md

Use Type Operations and Generic Types to Avoid Repeating Yourself

Overview

Apply DRY (Don't Repeat Yourself) to types, not just code.

Type duplication causes the same problems as code duplication: inconsistency, maintenance burden, and bugs. Use TypeScript's type operations to derive types from other types.

When to Use This Skill

  • Copying fields between interfaces
  • Multiple types share common properties
  • Want one type to be a subset of another
  • Types should stay in sync automatically
  • Need to create optional/partial versions of types

The Iron Rule

NEVER copy-paste type definitions. Derive types from a single source of truth.

Remember:

  • extends for adding fields
  • Pick<T, K> for selecting fields
  • Partial<T> for making fields optional
  • keyof for getting key types
  • typeof for deriving types from values

Detection: The Copied Type Problem

If you see similar types diverging:

// ❌ Duplicated type definitions
interface Person {
  firstName: string;
  lastName: string;
}

interface PersonWithBirthDate {
  firstName: string;   // Duplicated!
  lastName: string;    // Duplicated!
  birth: Date;
}

What if you add middleName to Person? Now they're out of sync.

Basic Techniques

Use extends to Add Fields

// ✅ Derive from base type
interface Person {
  firstName: string;
  lastName: string;
}

interface PersonWithBirthDate extends Person {
  birth: Date;
}

Use Pick to Select Fields

interface State {
  userId: string;
  pageTitle: string;
  recentFiles: string[];
  pageContents: string;
}

// ✅ Select only the fields you need
type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>;

Use Partial for Optional Versions

interface Options {
  width: number;
  height: number;
  color: string;
}

class UIWidget {
  constructor(init: Options) { /* ... */ }

  // ✅ All fields optional for updates
  update(options: Partial<Options>) { /* ... */ }
}

Use keyof for Key Types

type OptionsKeys = keyof Options;
//   ^? type OptionsKeys = "width" | "height" | "color"

Use typeof to Derive from Values

const DEFAULTS = {
  width: 640,
  height: 480,
  color: '#00FF00',
};

// ✅ Type derived from value
type Options = typeof DEFAULTS;
//   ^? type Options = { width: number; height: number; color: string; }

Standard Library Utility Types

UtilityPurposeExample
Pick<T, K>Select properties`Pick<User, 'id' \'name'>`
Omit<T, K>Remove propertiesOmit<User, 'password'>
Partial<T>Make all optionalPartial<Config>
Required<T>Make all requiredRequired<PartialConfig>
Readonly<T>Make all readonlyReadonly<State>
ReturnType<F>Function return typeReturnType<typeof fn>
Parameters<F>Function paramsParameters<typeof fn>

Advanced Patterns

Mapped Types

// Create optional version manually
type OptionsUpdate = {
  [K in keyof Options]?: Options[K]
};

// Equivalent to Partial<Options>

Indexing into Union Types

interface SaveAction { type: 'save'; /* ... */ }
interface LoadAction { type: 'load'; /* ... */ }
type Action = SaveAction | LoadAction;

// ✅ Extract discriminant type
type ActionType = Action['type'];
//   ^? type ActionType = "save" | "load"

ReturnType for Function Results

function getUserInfo(userId: string) {
  return {
    userId,
    name,
    age,
    // ... many fields
  };
}

// ✅ Derive type from function
type UserInfo = ReturnType<typeof getUserInfo>;

Key Remapping with as

interface ShortToLong {
  q: 'search';
  n: 'numberOfResults';
}

// Invert the mapping
type LongToShort = {
  [K in keyof ShortToLong as ShortToLong[K]]: K
};
//   ^? type LongToShort = { search: "q"; numberOfResults: "n"; }

Homomorphic Mapped Types

Mapped types preserve modifiers when using keyof:

interface Customer {
  /** How the customer would like to be addressed. */
  title?: string;
  /** Complete name as entered in the system. */
  readonly name: string;
}

// ✅ Preserves optional and readonly
type PickTitle = Pick<Customer, 'title'>;
//   ^? type PickTitle = { title?: string; }

type PickName = Pick<Customer, 'name'>;
//   ^? type PickName = { readonly name: string; }

When NOT to Apply DRY

Don't factor out types that are only coincidentally similar:

// ❌ Don't do this - coincidental similarity
interface NamedAndIdentified {
  id: number;
  name: string;
}

interface Product extends NamedAndIdentified {
  priceDollars: number;
}

interface Customer extends NamedAndIdentified {
  address: string;
}

Why not? Product.id and Customer.id are semantically different:

  • Customer.id might become a UUID
  • Product.name and Customer.name might evolve differently

Rule of thumb: If you can't name it meaningfully, it's probably premature abstraction.

Common Patterns

Base + Extensions

// Shared base
interface Vertebrate {
  weightGrams: number;
  color: string;
  isNocturnal: boolean;
}

// Specific extensions
interface Bird extends Vertebrate {
  wingspanCm: number;
}

interface Mammal extends Vertebrate {
  eatsGardenPlants: boolean;
}

Input/Output Types

// Full type
interface User {
  id: string;
  email: string;
  name: string;
  createdAt: Date;
}

// Input type derived
type CreateUserInput = Pick<User, 'email' | 'name'>;

// Or with Omit
type UpdateUserInput = Partial<Omit<User, 'id' | 'createdAt'>>;

Function Signatures

// ✅ Factor out common signatures
type HTTPFunction = (url: string, opts: Options) => Promise<Response>;

const get: HTTPFunction = (url, opts) => { /* ... */ };
const post: HTTPFunction = (url, opts) => { /* ... */ };

Pressure Resistance Protocol

1. "Copy-Paste Is Faster"

Pressure: "Just duplicate the type, it's quicker"

Response: Technical debt accumulates. Types will drift apart.

Action: Take the time to use extends, Pick, or other derivations.

2. "The Types Are Different Enough"

Pressure: "They share fields by coincidence"

Response: Good point - verify they're semantically the same first.

Action: Only factor out types that represent the same concept.

Red Flags - STOP and Reconsider

  • Copy-pasting interface fields
  • Multiple types with identical property subsets
  • Updating one type but forgetting another
  • Types named "...WithX" that duplicate base type

Common Rationalizations (All Invalid)

ExcuseReality
"It's just a few fields"A few fields × many types = maintenance nightmare
"I'll remember to update both"You won't, or your teammates won't
"The derivation is confusing"Less confusing than debugging drift

Quick Reference

// Adding fields
interface Extended extends Base { newField: T; }

// Selecting fields
type Subset = Pick<Full, 'a' | 'b'>;

// Removing fields
type WithoutPassword = Omit<User, 'password'>;

// Making optional
type Updates = Partial<Options>;

// Getting return type
type Result = ReturnType<typeof myFunction>;

// Getting key union
type Keys = keyof MyInterface;

The Bottom Line

Derive types from a single source of truth.

Use TypeScript's type operations (extends, Pick, Partial, keyof, typeof, mapped types) to express relationships between types. This keeps types in sync and reduces maintenance burden. But only apply DRY when types are semantically related, not just structurally similar.

Reference

Based on "Effective TypeScript" by Dan Vanderkam, Item 15: Use Type Operations and Generic Types to Avoid Repeating Yourself.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.73%
按下载量换算28

Claude

26.58%
按下载量换算20

Cursor

19.27%
按下载量换算14

Gemini CLI

9.76%
按下载量换算7

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills