ワンクリックで
hide-unsafe-assertions
Use when type assertions are necessary. Use when function implementations need any. Use when hiding unsafe code.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Use when type assertions are necessary. Use when function implementations need any. Use when hiding unsafe code.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when defining global types. Use when augmenting window. Use when typing environment variables. Use when working with build-time constants. Use when configuring type definitions.
Use when migrating JavaScript to TypeScript. Use when gradually adopting TypeScript. Use when working with mixed codebases. Use when converting large projects. Use when teams are learning TypeScript.
Use when writing asynchronous code. Use when tempted to use callbacks. Use when composing multiple async operations.
Use when creating types from example data. Use when types don't match all cases. Use when API responses vary.
Use when writing type annotations on variables. Use when TypeScript can infer the type. Use when code feels cluttered with types.
Use when defining array-like types. Use when tempted to use number as index type. Use when understanding array keys.
| name | hide-unsafe-assertions |
| description | Use when type assertions are necessary. Use when function implementations need any. Use when hiding unsafe code. |
Keep type signatures clean; hide assertions in implementations.
If a function needs a type assertion or any internally, that's OK - as long as the public signature is correct. Users see a well-typed API; the unsafe code is contained.
Never compromise type signatures for implementation convenience.
Hide assertions inside well-typed functions.
Remember:
// Bad: unsafe return type exposes any
async function fetchPeak(peakId: string): Promise<unknown> {
return checkedFetchJSON(`/api/peaks/${peakId}`);
}
// Every caller must assert:
const peak = await fetchPeak('denali') as MountainPeak; // Tedious!
async function fetchPeak(peakId: string): Promise<MountainPeak> {
return checkedFetchJSON(`/api/peaks/${peakId}`) as Promise<MountainPeak>;
}
// Callers get clean types:
const peak = await fetchPeak('denali');
// ^? MountainPeak
The assertion is hidden inside; callers get a clean API.
async function fetchPeak(peakId: string): Promise<MountainPeak> {
const maybePeak = await checkedFetchJSON(`/api/peaks/${peakId}`);
// Validation adds safety to the assertion
if (!maybePeak || typeof maybePeak !== 'object' || !('name' in maybePeak)) {
throw new Error(`Invalid peak data: ${JSON.stringify(maybePeak)}`);
}
return maybePeak as MountainPeak;
}
Now the assertion has runtime backup.
function shallowEqual(a: object, b: object): boolean {
for (const [k, aVal] of Object.entries(a)) {
if (!(k in b) || aVal !== b[k]) {
// ~~~~
// Element implicitly has an 'any' type
return false;
}
}
return Object.keys(a).length === Object.keys(b).length;
}
We know k in b is true, but TypeScript doesn't connect this to b[k].
// DON'T: weakening b to any exposes unsafety
function shallowEqual(a: object, b: any): boolean {
// ...
}
shallowEqual({x: 1}, null); // No error! Crashes at runtime.
function shallowEqual(a: object, b: object): boolean {
for (const [k, aVal] of Object.entries(a)) {
// Hidden assertion - we've checked k in b
if (!(k in b) || aVal !== (b as any)[k]) {
return false;
}
}
return Object.keys(a).length === Object.keys(b).length;
}
shallowEqual({x: 1}, null);
// ~~~~
// Argument of type 'null' is not assignable to parameter of type 'object'.
Type signature stays clean; assertion is narrowly scoped.
// Overload presents clean signature to callers
async function fetchPeak(peakId: string): Promise<MountainPeak>;
async function fetchPeak(peakId: string): Promise<unknown> {
return checkedFetchJSON(`/api/peaks/${peakId}`);
}
const denali = fetchPeak('denali');
// ^? Promise<MountainPeak>
The implementation returns unknown, but callers see MountainPeak.
// DON'T: Assertion on whole object
const config: Config = {
a: 1,
b: 2,
c: { key: value }
} as any; // No checking on a, b!
// DO: Assertion only on problem area
const config: Config = {
a: 1,
b: 2, // These are still checked
c: { key: value as any } // Only this is unsafe
};
function shallowEqual(a: object, b: object): boolean {
for (const [k, aVal] of Object.entries(a)) {
// `(b as any)[k]` is safe because we've verified `k in b`
if (!(k in b) || aVal !== (b as any)[k]) {
return false;
}
}
return Object.keys(a).length === Object.keys(b).length;
}
Comments help future maintainers understand the assertion.
Functions with hidden assertions need extra testing:
describe('fetchPeak', () => {
it('handles valid peak data', async () => {
const peak = await fetchPeak('denali');
expect(peak.name).toBe('Denali');
expect(peak.elevationMeters).toBe(6190);
});
it('throws on invalid data', async () => {
mockFetch({ invalid: 'data' });
await expect(fetchPeak('unknown')).rejects.toThrow('Invalid peak');
});
it('handles missing fields', async () => {
mockFetch({ name: 'Partial' }); // Missing fields
await expect(fetchPeak('partial')).rejects.toThrow();
});
});
Pressure: "Make it return unknown to avoid the assertion"
Response: That pushes unsafety to every caller.
Action: Keep clean signature; hide assertion in implementation.
Pressure: "We should avoid assertions entirely"
Response: Sometimes they're necessary. Contained and tested is OK.
Action: Hide, validate, document, and test.
any or unknown for convenience| Excuse | Reality |
|---|---|
| "It's more honest" | Pushing unsafety to callers is worse |
| "Assertions are bad" | Contained assertions are fine |
| "Users can narrow" | Users shouldn't have to |
// DON'T: Expose unsafety in signature
function fetch(): Promise<unknown> { ... }
const data = await fetch() as Data; // Assertion at every call site
// DO: Hide assertion in implementation
function fetch(): Promise<Data> {
return api.fetch() as Promise<Data>; // Hidden, one place
}
// DO: Narrow scope
const obj = { a: value as any }; // Not: whole object as any
// DO: Document and test
// This assertion is valid because... [explanation]
return data as Data;
Hide unsafe code; expose clean types.
Type assertions and any types are sometimes necessary. Keep them in function implementations, not signatures. Document why they're valid. Test thoroughly.
Based on "Effective TypeScript" by Dan Vanderkam, Item 45: Hide Unsafe Type Assertions in Well-Typed Functions.