skills/typescript/typescript/SKILL.md
TypeScript code style, type co-location, constant naming conventions, and arktype patterns. Use when writing TypeScript code, defining types, creating constants, or working with arktype schemas.
npx skillsauth add aiagentskills/skills typescriptInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
type instead of interface in TypeScript..filter() callbacks. Don't add manual type assertions:
// Good - TypeScript infers the narrowed type automatically
const filtered = items.filter((x) => x !== undefined);
// Bad - unnecessary type predicate
const filtered = items.filter((x): x is NonNullable<typeof x> => x !== undefined);
import Component from '../Component.svelte' to import Component from '$lib/components/Component.svelte')function myFunction() {
const helper = () => {
/* ... */
};
return { helper };
}
Use:
function myFunction() {
return {
helper() {
/* ... */
},
};
}
Don't create generic type files like $lib/types/models.ts. This creates unclear dependencies and makes code harder to maintain.
// $lib/types/models.ts - Generic bucket for unrelated types
export type LocalModelConfig = { ... };
export type UserModel = { ... };
export type SessionModel = { ... };
// $lib/services/transcription/local/types.ts - Co-located with service
export type LocalModelConfig = { ... };
// $lib/services/user/types.ts - Co-located with user service
export type UserModel = { ... };
[service-folder]/types.tstypes.ts$lib/types/[specific-name].ts| Pattern | Suffix | Description | Example |
|---------|--------|-------------|---------|
| Simple values (source of truth) | Plural noun with unit | Raw values array | BITRATES_KBPS, SAMPLE_RATES |
| Rich array (source of truth) | Plural noun | Contains all metadata | PROVIDERS, RECORDING_MODE_OPTIONS |
| IDs only (for validation) | _IDS | Derived from rich array | PROVIDER_IDS |
| UI options {value, label} | _OPTIONS | For dropdowns/selects | BITRATE_OPTIONS, SAMPLE_RATE_OPTIONS |
| Label map | _TO_LABEL (singular) | Record<Id, string> | LANGUAGES_TO_LABEL |
Use when the label can be computed from the value:
// constants/audio/bitrate.ts
export const BITRATES_KBPS = ['16', '32', '64', '128'] as const;
export const BITRATE_OPTIONS = BITRATES_KBPS.map((bitrate) => ({
value: bitrate,
label: `${bitrate} kbps`,
}));
Use when labels need richer information than the value alone:
// constants/audio/sample-rate.ts
export const SAMPLE_RATES = ['16000', '44100', '48000'] as const;
const SAMPLE_RATE_METADATA: Record<SampleRate, { shortLabel: string; description: string }> = {
'16000': { shortLabel: '16 kHz', description: 'Optimized for speech' },
'44100': { shortLabel: '44.1 kHz', description: 'CD quality' },
'48000': { shortLabel: '48 kHz', description: 'Studio quality' },
};
export const SAMPLE_RATE_OPTIONS = SAMPLE_RATES.map((rate) => ({
value: rate,
label: `${SAMPLE_RATE_METADATA[rate].shortLabel} - ${SAMPLE_RATE_METADATA[rate].description}`,
}));
Use when options have extra fields beyond value/label (e.g., icon, desktopOnly):
// constants/audio/recording-modes.ts
export const RECORDING_MODES = ['manual', 'vad', 'upload'] as const;
export type RecordingMode = (typeof RECORDING_MODES)[number];
export const RECORDING_MODE_OPTIONS = [
{ label: 'Manual', value: 'manual', icon: 'mic', desktopOnly: false },
{ label: 'Voice Activated', value: 'vad', icon: 'mic-voice', desktopOnly: false },
{ label: 'Upload File', value: 'upload', icon: 'upload', desktopOnly: false },
] as const satisfies { label: string; value: RecordingMode; icon: string; desktopOnly: boolean }[];
// Derive IDs for validation if needed
export const RECORDING_MODE_IDS = RECORDING_MODE_OPTIONS.map(o => o.value);
| Scenario | Pattern | |----------|---------| | Label = formatted value (e.g., "128 kbps") | Simple Values -> Derived | | Label needs separate data (e.g., "16 kHz - Optimized for speech") | Values + Metadata | | Options have extra UI fields (icon, description, disabled) | Rich Array | | Platform-specific or runtime-conditional content | Keep inline in component |
PROVIDERS, MODES, LANGUAGESBITRATES_KBPS, SAMPLE_RATES_VALUES suffix_OPTIONS suffix: BITRATE_OPTIONS, SAMPLE_RATE_OPTIONS_IDS suffix: PROVIDER_IDS_TO_LABEL suffix: LANGUAGES_TO_LABELLANGUAGES_TO_LABEL[lang] = "get the label for this language"SCREAMING_SNAKE_CASE for exported constantscamelCase for constant objects/arraysOptions arrays should be co-located with their source array in the same file. Avoid creating options inline in Svelte components; import pre-defined options instead.
Exception: Keep options inline when they have platform-specific or runtime-conditional content that would require importing platform constants into the data module.
When writing factory functions that take options objects, destructure directly in the function signature instead of in the function body. This is the established pattern in the codebase.
// DON'T: Extra line of ceremony
function createSomething(opts: { foo: string; bar?: number }) {
const { foo, bar = 10 } = opts; // Unnecessary extra line
return { foo, bar };
}
// DO: Destructure directly in parameters
function createSomething({ foo, bar = 10 }: { foo: string; bar?: number }) {
return { foo, bar };
}
const generics: TypeScript literal inference works correctly:
function select<const TOptions extends readonly string[]>({
options,
nullable = false,
}: {
options: TOptions;
nullable?: boolean;
}) { ... }
undefined" ('key' in opts)opts object to other functions// From packages/epicenter/src/core/schema/columns.ts
export function select<const TOptions extends readonly [string, ...string[]]>({
options,
nullable = false,
default: defaultValue,
}: {
options: TOptions;
nullable?: boolean;
default?: TOptions[number];
}): SelectColumnSchema<TOptions, boolean> {
return { type: 'select', nullable, options, default: defaultValue };
}
// From apps/whispering/.../create-key-recorder.svelte.ts
export function createKeyRecorder({
pressedKeys,
onRegister,
onClear,
}: {
pressedKeys: PressedKeys;
onRegister: (keyCombination: KeyboardEventSupportedKey[]) => void;
onClear: () => void;
}) { ... }
| undefined for Optional PropertiesWhen defining optional properties in arktype schemas, always use the 'key?' syntax instead of | undefined unions. This is critical for JSON Schema conversion (used by OpenAPI/MCP).
// DON'T: Explicit undefined union - breaks JSON Schema conversion
const schema = type({
window_id: 'string | undefined',
url: 'string | undefined',
});
This produces invalid JSON Schema with anyOf: [{type: "string"}, {}] because undefined has no JSON Schema equivalent.
// DO: Optional property syntax - converts cleanly to JSON Schema
const schema = type({
'window_id?': 'string',
'url?': 'string',
});
This correctly omits properties from the required array in JSON Schema.
| Syntax | TypeScript Behavior | JSON Schema |
|--------|---------------------|-------------|
| key: 'string \| undefined' | Required prop, accepts string or undefined | Broken (triggers fallback) |
| 'key?': 'string' | Optional prop, accepts string | Clean (omitted from required) |
Both behave similarly in TypeScript, but only the ? syntax converts correctly to JSON Schema for OpenAPI documentation and MCP tool schemas.
documentation
Guides using bun.sys for system calls and file I/O in Zig. Use when implementing file operations instead of std.fs or std.posix.
development
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
Guides writing HMR/Dev Server tests in test/bake/. Use when creating or modifying dev server, hot reloading, or bundling tests.