Comprehensive TypeScript guidance covering compiler configuration, advanced types, utility types, type guards, strict mode workflows, and documentation patterns; use when configuring tsconfig, designing complex generics, making illegal states unrepresentable, fixing type errors, or writing testable and maintainable type-safe APIs.
Overall
score
99%
Does it follow best practices?
Validation for skill structure
Use typeof to narrow primitive types:
function process(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase(); // value is string here
} else {
return value.toFixed(2); // value is number here
}
}Narrow to class instances:
class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function speak(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // animal is Dog here
} else {
animal.meow(); // animal is Cat here
}
}Check for property existence:
interface User {
name: string;
email: string;
}
interface Guest {
name: string;
sessionId: string;
}
function greet(person: User | Guest) {
if ("email" in person) {
console.log(person.email); // person is User here
} else {
console.log(person.sessionId); // person is Guest here
}
}Functions that return value is Type:
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
function process(value: unknown) {
if (isString(value)) {
return value.toUpperCase(); // value is string
}
if (isNumber(value)) {
return value.toFixed(2); // value is number
}
}function isArray<T>(value: unknown): value is T[] {
return Array.isArray(value);
}
function isRecord<T extends object>(
value: unknown
): value is Record<string, T> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}type Success = { status: "success"; data: string };
type Failure = { status: "failure"; error: Error };
type Result = Success | Failure;
function handle(result: Result) {
switch (result.status) {
case "success":
console.log(result.data); // result is Success
break;
case "failure":
console.error(result.error); // result is Failure
break;
}
}Functions that throw or narrow types:
function assert(condition: unknown): asserts condition {
if (!condition) {
throw new Error("Assertion failed");
}
}
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error("Not a string");
}
}
function process(value: unknown) {
assertIsString(value);
// value is string from this point forward
return value.toUpperCase();
}function assertIsDefined<T>(
value: T,
message?: string
): asserts value is NonNullable<T> {
if (value === null || value === undefined) {
throw new Error(message ?? "Value is null or undefined");
}
}
function process(value: string | null) {
assertIsDefined(value);
// value is string here
return value.toUpperCase();
}function process(value: string | number, check: string | number) {
if (value === check) {
// Both value and check are narrowed to their intersection type
console.log(value.toFixed(2)); // Only works if both can be number
}
}function process(value: string | null | undefined) {
if (value) {
// value is string here (null and undefined are falsy)
return value.toUpperCase();
}
}
// Be careful with falsy values
function handleNumber(value: number | null) {
if (value) {
// This excludes 0, which might not be intended
return value.toFixed(2);
}
}function process(value: string | null | undefined) {
if (value !== null && value !== undefined) {
return value.toUpperCase(); // value is string
}
// Or use != (non-strict)
if (value != null) {
return value.toUpperCase(); // value is string (excludes null and undefined)
}
}function process(value: string | string[]) {
if (Array.isArray(value)) {
return value.join(", "); // value is string[]
} else {
return value.toUpperCase(); // value is string
}
}function hasProperty<K extends string>(
obj: unknown,
key: K
): obj is Record<K, unknown> {
return typeof obj === "object" && obj !== null && key in obj;
}
function process(value: unknown) {
if (hasProperty(value, "name") && typeof value.name === "string") {
console.log(value.name.toUpperCase());
}
}interface Named {
name: string;
}
function isNamed<T>(value: T): value is T & Named {
return (
typeof value === "object" &&
value !== null &&
"name" in value &&
typeof (value as any).name === "string"
);
}type Shape = Circle | Square | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.size ** 2;
case "triangle":
return (shape.base * shape.height) / 2;
default:
// Ensures all cases are handled
const _exhaustive: never = shape;
throw new Error(`Unhandled shape: ${_exhaustive}`);
}
}TypeScript tracks type narrowing through control flow:
function process(value: string | number | null) {
if (value === null) {
return "No value";
}
// value is string | number here
if (typeof value === "string") {
return value.toUpperCase();
}
// value is number here
return value.toFixed(2);
}typeof null === "object"== vs === in narrowingInstall with Tessl CLI
npx tessl i pantheon-ai/typescript-advancedreferences