Prerequisites
- Understanding of template literal types ๐ฏ
- Knowledge of string literal types ๐
- Familiarity with type-level programming โก
What you'll learn
- Master all intrinsic string manipulation types ๐๏ธ
- Build type-safe string transformation utilities ๐ง
- Create elegant naming convention systems ๐จ
- Design sophisticated string validation patterns ๐ก๏ธ
๐ฏ Introduction
Welcome to the wonderful world of intrinsic string manipulation types! ๐ These are TypeScriptโs built-in string transformers that work like magic spells โจ at the type level - they can transform any string type into uppercase, lowercase, capitalized, or uncapitalized versions instantly!
Youโre about to discover TypeScript 4.1โs most convenient string manipulation features. Whether youโre building consistent naming conventions ๐ท๏ธ, creating type-safe CSS class generators ๐จ, or designing elegant API interfaces ๐, these intrinsic types will make your string transformations effortless and perfectly type-safe.
By the end of this tutorial, youโll be transforming strings at the type level like a wizard casting transformation spells! ๐งโโ๏ธ Letโs unlock these magical string powers! ๐
๐ Understanding Intrinsic String Types
๐ค What are Intrinsic String Types?
Intrinsic string manipulation types are TypeScriptโs built-in utility types that transform string literal types. Think of them as magical functions ๐ช that take any string type and return a transformed version - but all at compile time!
The four intrinsic string types are:
Uppercase<T>
- Transforms to ALL UPPERCASELowercase<T>
- Transforms to all lowercaseCapitalize<T>
- Transforms to Title Case (first letter uppercase)Uncapitalize<T>
- Transforms to lowerCase (first letter lowercase)
// ๐ฏ Basic intrinsic string transformations
type ShoutedGreeting = Uppercase<"hello world">; // "HELLO WORLD"
type WhisperedGreeting = Lowercase<"HELLO WORLD">; // "hello world"
type ProperGreeting = Capitalize<"hello world">; // "Hello world"
type CasualGreeting = Uncapitalize<"Hello World">; // "hello World"
๐จ Your First String Transformations
Letโs start with simple examples to see the power:
// ๐ฏ Working with string literals
type OriginalMessage = "welcome to typescript";
type LoudMessage = Uppercase<OriginalMessage>; // "WELCOME TO TYPESCRIPT"
type QuietMessage = Lowercase<OriginalMessage>; // "welcome to typescript" (unchanged)
type PoliteMessage = Capitalize<OriginalMessage>; // "Welcome to typescript"
type CasualMessage = Uncapitalize<OriginalMessage>; // "welcome to typescript" (unchanged)
// ๐ Working with union types
type Colors = "red" | "green" | "blue";
type LoudColors = Uppercase<Colors>; // "RED" | "GREEN" | "BLUE"
type QuietColors = Lowercase<Colors>; // "red" | "green" | "blue" (unchanged)
type ProperColors = Capitalize<Colors>; // "Red" | "Green" | "Blue"
// โจ Working with template literals
type EventName = `on${Capitalize<Colors>}Change`;
// Result: "onRedChange" | "onGreenChange" | "onBlueChange"
type CssClass = `${Lowercase<Colors>}-theme`;
// Result: "red-theme" | "green-theme" | "blue-theme"
๐ก The Magic: These types work with any string literal type and automatically distribute over union types!
๐ง Uppercase<T>: SHOUTING TYPES
๐ Making Everything LOUD
Uppercase<T>
transforms any string type to ALL UPPERCASE LETTERS:
// ๐จ Basic uppercase transformations
type DatabaseTable = Uppercase<"users">; // "USERS"
type HttpMethod = Uppercase<"get" | "post" | "put" | "delete">;
// Result: "GET" | "POST" | "PUT" | "DELETE"
// ๐ Practical example: Environment variables
type EnvKey = "api_url" | "database_host" | "secret_key";
type EnvVariable = Uppercase<EnvKey>;
// Result: "API_URL" | "DATABASE_HOST" | "SECRET_KEY"
// โจ Working with template literals
type LogLevel = "info" | "warn" | "error";
type LogMessage<T extends string> = `[${Uppercase<T>}] ${string}`;
type InfoLog = LogMessage<"info">; // "[INFO] ${string}"
type ErrorLog = LogMessage<"error">; // "[ERROR] ${string}"
// ๐งช Real-world usage: Constants generation
interface AppConfig {
apiUrl: string;
databaseHost: string;
secretKey: string;
}
type ConfigConstants = {
[K in keyof AppConfig as Uppercase<string & K>]: string;
};
// Result: {
// APIURL: string;
// DATABASEHOST: string;
// SECRETKEY: string;
// }
๐ฎ Advanced Uppercase Patterns
// ๐ฏ HTTP status code constants
type HttpStatus = "ok" | "not_found" | "internal_server_error";
type HttpStatusConstant = `HTTP_${Uppercase<HttpStatus>}`;
// Result: "HTTP_OK" | "HTTP_NOT_FOUND" | "HTTP_INTERNAL_SERVER_ERROR"
// ๐ง Redux Action Types
type ActionType = "add_user" | "remove_user" | "update_user";
type ReduxAction = Uppercase<ActionType>;
// Result: "ADD_USER" | "REMOVE_USER" | "UPDATE_USER"
// ๐จ CSS Custom Property Names
type CssProperty = "primary_color" | "font_size" | "border_radius";
type CssVariableName = `--${Uppercase<CssProperty>}`;
// Result: "--PRIMARY_COLOR" | "--FONT_SIZE" | "--BORDER_RADIUS"
// โจ Database column naming
type ModelField = "firstName" | "lastName" | "emailAddress";
type DatabaseColumn = Uppercase<ModelField>;
// Result: "FIRSTNAME" | "LASTNAME" | "EMAILADDRESS"
๐ฝ Lowercase<T>: whispering types
๐ Making Everything quiet
Lowercase<T>
transforms any string type to all lowercase letters:
// ๐จ Basic lowercase transformations
type ShoutedCommand = "CREATE" | "READ" | "UPDATE" | "DELETE";
type QuietCommand = Lowercase<ShoutedCommand>;
// Result: "create" | "read" | "update" | "delete"
// ๐ Practical example: URL slugs
type PageTitle = "Home Page" | "About Us" | "Contact Form";
type UrlSlug = Lowercase<PageTitle>;
// Result: "home page" | "about us" | "contact form"
// โจ File extensions
type ImageFormat = "PNG" | "JPG" | "GIF" | "SVG";
type FileExtension = `.${Lowercase<ImageFormat>}`;
// Result: ".png" | ".jpg" | ".gif" | ".svg"
// ๐งช CSS class names
type ComponentName = "Button" | "Modal" | "Dropdown";
type CssClassName = Lowercase<ComponentName>;
// Result: "button" | "modal" | "dropdown"
๐ฎ Advanced Lowercase Patterns
// ๐ฏ Domain validation
type TLD = "COM" | "NET" | "ORG" | "EDU";
type DomainExtension = `.${Lowercase<TLD>}`;
// Result: ".com" | ".net" | ".org" | ".edu"
// ๐ง HTML tag names
type HtmlTag = "DIV" | "SPAN" | "BUTTON" | "INPUT";
type ValidHtmlTag = Lowercase<HtmlTag>;
// Result: "div" | "span" | "button" | "input"
// ๐จ API endpoint naming
type ResourceName = "User" | "Post" | "Comment";
type ApiEndpoint = `/api/${Lowercase<ResourceName>}s`;
// Result: "/api/users" | "/api/posts" | "/api/comments"
// โจ Package naming convention
type PackageName = "React-Router" | "Express-Session" | "Body-Parser";
type NpmPackage = Lowercase<PackageName>;
// Result: "react-router" | "express-session" | "body-parser"
๐ฉ Capitalize<T>: Proper Case Types
๐ Making the First Letter Proper
Capitalize<T>
transforms the first letter to uppercase while keeping the rest unchanged:
// ๐จ Basic capitalization
type greeting = "hello world";
type Greeting = Capitalize<greeting>; // "Hello world"
type command = "start" | "stop" | "restart";
type Command = Capitalize<command>; // "Start" | "Stop" | "Restart"
// ๐ Practical example: Method names
type Action = "create" | "update" | "delete";
type MethodName = `handle${Capitalize<Action>}`;
// Result: "handleCreate" | "handleUpdate" | "handleDelete"
// โจ Component naming
type elementType = "button" | "input" | "select";
type ComponentName = Capitalize<elementType>;
// Result: "Button" | "Input" | "Select"
// ๐งช Property names
type fieldName = "firstName" | "lastName" | "emailAddress";
type LabelText = `${Capitalize<fieldName>}:`;
// Result: "FirstName:" | "LastName:" | "EmailAddress:"
๐ฎ Advanced Capitalize Patterns
// ๐ฏ React component prop names
type eventType = "click" | "hover" | "focus" | "blur";
type EventHandlerProp = `on${Capitalize<eventType>}`;
// Result: "onClick" | "onHover" | "onFocus" | "onBlur"
// ๐ง API method generation
type httpMethod = "get" | "post" | "put" | "delete";
type ApiMethod<T extends string> = `${httpMethod}${Capitalize<T>}`;
type UserMethods = ApiMethod<"users">; // "getUsers" | "postUsers" | "putUsers" | "deleteUsers"
// ๐จ CSS modifier classes
type state = "active" | "disabled" | "loading";
type ModifierClass = `btn--${state}` | `btn--${Capitalize<state>}`;
// Result: "btn--active" | "btn--Active" | "btn--disabled" | "btn--Disabled" | etc.
// โจ Validation message keys
type validationType = "required" | "email" | "minLength";
type ValidationKey = `validation.${Capitalize<validationType>}`;
// Result: "validation.Required" | "validation.Email" | "validation.MinLength"
๐ฝ Uncapitalize<T>: Casual Case Types
๐ Making the First Letter Lowercase
Uncapitalize<T>
transforms the first letter to lowercase while keeping the rest unchanged:
// ๐จ Basic uncapitalization
type ProperName = "Alice" | "Bob" | "Charlie";
type CasualName = Uncapitalize<ProperName>; // "alice" | "bob" | "charlie"
type ClassName = "Button" | "Modal" | "Dropdown";
type CssClass = Uncapitalize<ClassName>; // "button" | "modal" | "dropdown"
// ๐ Practical example: camelCase conversion
type PascalCase = "UserProfile" | "PostList" | "CommentForm";
type CamelCase = Uncapitalize<PascalCase>; // "userProfile" | "postList" | "commentForm"
// โจ JSON property names
type ApiResponse = "UserId" | "UserName" | "UserEmail";
type JsonProperty = Uncapitalize<ApiResponse>; // "userId" | "userName" | "userEmail"
// ๐งช Variable naming
type ConstantName = "API_URL" | "DATABASE_HOST";
type VariableName = Uncapitalize<Lowercase<ConstantName>>; // "aPI_URL" | "dATABASE_HOST"
๐ฎ Advanced Uncapitalize Patterns
// ๐ฏ Object property normalization
type DatabaseField = "FirstName" | "LastName" | "EmailAddress";
type ObjectProperty = Uncapitalize<DatabaseField>;
// Result: "firstName" | "lastName" | "emailAddress"
// ๐ง Function parameter names
type ConfigOption = "ApiUrl" | "DatabaseHost" | "SecretKey";
type ParameterName = Uncapitalize<ConfigOption>;
// Result: "apiUrl" | "databaseHost" | "secretKey"
// ๐จ CSS custom property transformation
type CssVariable = "--Primary-Color" | "--Font-Size";
type CssPropertyName = Uncapitalize<CssVariable>;
// Result: "--primary-Color" | "--font-Size"
// โจ Event handler naming
type EventType = "Click" | "Hover" | "Focus";
type EventHandler = `on${EventType}` | `handle${EventType}`;
type CasualHandler = Uncapitalize<EventHandler>;
// Result: "onClick" | "handleClick" | "onHover" | "handleHover" | etc.
๐ก Practical Examples
๐ Example 1: Type-Safe CSS Class Generator
Letโs build a sophisticated CSS class naming system:
// ๐ฏ Component base names
type ComponentBase = "button" | "card" | "modal" | "input" | "dropdown";
// ๐จ Component variants
type ComponentVariant = "primary" | "secondary" | "danger" | "success";
// ๐ Component sizes
type ComponentSize = "small" | "medium" | "large" | "xlarge";
// โจ Component states
type ComponentState = "disabled" | "active" | "loading" | "error";
// ๐งช CSS class generation system
type CssClassSystem = {
// Base classes (lowercase)
base: Lowercase<ComponentBase>;
// Variant classes (lowercase with prefix)
variant: `${Lowercase<ComponentBase>}--${Lowercase<ComponentVariant>}`;
// Size classes (lowercase with prefix)
size: `${Lowercase<ComponentBase>}--${Lowercase<ComponentSize>}`;
// State classes (lowercase with prefix)
state: `${Lowercase<ComponentBase>}--${Lowercase<ComponentState>}`;
// BEM modifier classes
modifier: `${Lowercase<ComponentBase>}__${Lowercase<ComponentVariant>}`;
// Utility classes (uppercase for constants)
utility: `u-${Uppercase<ComponentState>}`;
};
// ๐ฎ Type-safe CSS class builder
class CssClassBuilder {
private classes: string[] = [];
// ๐ฏ Add base class
base<T extends ComponentBase>(component: T): this {
this.classes.push(component.toLowerCase());
return this;
}
// ๐จ Add variant class
variant<T extends ComponentBase, V extends ComponentVariant>(
component: T,
variant: V
): this {
this.classes.push(`${component.toLowerCase()}--${variant.toLowerCase()}`);
return this;
}
// ๐ Add size class
size<T extends ComponentBase, S extends ComponentSize>(
component: T,
size: S
): this {
this.classes.push(`${component.toLowerCase()}--${size.toLowerCase()}`);
return this;
}
// โจ Add state class
state<T extends ComponentBase, S extends ComponentState>(
component: T,
state: S
): this {
this.classes.push(`${component.toLowerCase()}--${state.toLowerCase()}`);
return this;
}
// ๐งช Add utility class
utility<S extends ComponentState>(state: S): this {
this.classes.push(`u-${state.toUpperCase()}`);
return this;
}
// ๐ฏ Build final class string
build(): string {
return this.classes.join(' ');
}
// ๐ง Reset builder
reset(): this {
this.classes = [];
return this;
}
}
// ๐ฎ Advanced CSS class generation with intrinsic types
type CssClassGenerator<T extends ComponentBase> = {
[K in ComponentVariant as `${Lowercase<T>}${Capitalize<K>}`]: string;
} & {
[K in ComponentSize as `${Lowercase<T>}${Capitalize<K>}`]: string;
} & {
[K in ComponentState as `${Lowercase<T>}${Capitalize<K>}`]: string;
};
// ๐งช Usage examples
const cssBuilder = new CssClassBuilder();
// โ
Type-safe class building
const buttonClasses = cssBuilder
.base('button')
.variant('button', 'primary')
.size('button', 'large')
.state('button', 'active')
.build();
console.log('๐จ Button classes:', buttonClasses);
// Result: "button button--primary button--large button--active"
const modalClasses = cssBuilder
.reset()
.base('modal')
.variant('modal', 'secondary')
.utility('loading')
.build();
console.log('๐ญ Modal classes:', modalClasses);
// Result: "modal modal--secondary u-LOADING"
// ๐ Type-safe CSS-in-JS object
type CssInJsClasses<T extends ComponentBase> = {
[K in T as Lowercase<K>]: string;
} & {
[K in T as `${Lowercase<K>}Primary`]: string;
} & {
[K in T as `${Lowercase<K>}Secondary`]: string;
} & {
[K in T as `${Lowercase<K>}Large`]: string;
} & {
[K in T as `${Lowercase<K>}Small`]: string;
};
const cssClasses: CssInJsClasses<"button" | "modal"> = {
button: 'btn',
buttonPrimary: 'btn btn--primary',
buttonSecondary: 'btn btn--secondary',
buttonLarge: 'btn btn--large',
buttonSmall: 'btn btn--small',
modal: 'modal',
modalPrimary: 'modal modal--primary',
modalSecondary: 'modal modal--secondary',
modalLarge: 'modal modal--large',
modalSmall: 'modal modal--small'
};
console.log('๐ฏ CSS-in-JS classes:', cssClasses);
๐ฎ Example 2: API Naming Convention System
Letโs create a comprehensive API naming system:
// ๐ API resource definitions
type ApiResource = "user" | "post" | "comment" | "category" | "tag";
type HttpMethod = "get" | "post" | "put" | "delete" | "patch";
type ApiVersion = "v1" | "v2" | "v3";
// ๐ฏ API endpoint generation
type ApiEndpoint<
Version extends ApiVersion,
Resource extends ApiResource,
Method extends HttpMethod = "get"
> = `/${Lowercase<Version>}/${Lowercase<Resource>}s`;
// ๐จ API method naming
type ApiMethodName<
Method extends HttpMethod,
Resource extends ApiResource
> = `${Lowercase<Method>}${Capitalize<Resource>}`;
// ๐ API controller naming
type ApiControllerName<Resource extends ApiResource> = `${Capitalize<Resource>}Controller`;
// โจ API service naming
type ApiServiceName<Resource extends ApiResource> = `${Capitalize<Resource>}Service`;
// ๐งช API response type naming
type ApiResponseType<
Resource extends ApiResource,
Method extends HttpMethod
> = Method extends "get"
? `${Capitalize<Resource>}Response`
: Method extends "post"
? `Create${Capitalize<Resource>}Response`
: Method extends "put"
? `Update${Capitalize<Resource>}Response`
: Method extends "delete"
? `Delete${Capitalize<Resource>}Response`
: `${Capitalize<Resource>}Response`;
// ๐ฎ Complete API naming system
type ApiNamingSystem<R extends ApiResource> = {
// Endpoints
endpoints: {
[M in HttpMethod as Lowercase<M>]: ApiEndpoint<"v1", R, M>;
};
// Method names
methods: {
[M in HttpMethod as `${Lowercase<M>}${Capitalize<R>}`]: ApiMethodName<M, R>;
};
// Controller
controller: ApiControllerName<R>;
// Service
service: ApiServiceName<R>;
// Response types
responses: {
[M in HttpMethod as Lowercase<M>]: ApiResponseType<R, M>;
};
// Database table (uppercase)
table: Uppercase<R>;
// Model class (capitalized)
model: Capitalize<R>;
// Repository class
repository: `${Capitalize<R>}Repository`;
};
// ๐ง API generator class
class ApiGenerator<R extends ApiResource> {
constructor(private resource: R) {}
// ๐ฏ Generate endpoint
endpoint<M extends HttpMethod>(method: M, version: ApiVersion = "v1"): string {
return `/${version.toLowerCase()}/${this.resource.toLowerCase()}s`;
}
// ๐จ Generate method name
methodName<M extends HttpMethod>(method: M): string {
return `${method.toLowerCase()}${this.capitalize(this.resource)}`;
}
// ๐ Generate controller name
controllerName(): string {
return `${this.capitalize(this.resource)}Controller`;
}
// โจ Generate service name
serviceName(): string {
return `${this.capitalize(this.resource)}Service`;
}
// ๐งช Generate response type name
responseTypeName<M extends HttpMethod>(method: M): string {
const capitalizedResource = this.capitalize(this.resource);
switch (method) {
case "get":
return `${capitalizedResource}Response`;
case "post":
return `Create${capitalizedResource}Response`;
case "put":
return `Update${capitalizedResource}Response`;
case "delete":
return `Delete${capitalizedResource}Response`;
default:
return `${capitalizedResource}Response`;
}
}
// ๐ง Generate complete API structure
generateApiStructure(): ApiNamingSystem<R> {
const endpoints = {} as any;
const methods = {} as any;
const responses = {} as any;
const httpMethods: HttpMethod[] = ["get", "post", "put", "delete", "patch"];
httpMethods.forEach(method => {
endpoints[method] = this.endpoint(method);
methods[`${method}${this.capitalize(this.resource)}`] = this.methodName(method);
responses[method] = this.responseTypeName(method);
});
return {
endpoints,
methods,
responses,
controller: this.controllerName(),
service: this.serviceName(),
table: this.resource.toUpperCase() as Uppercase<R>,
model: this.capitalize(this.resource) as Capitalize<R>,
repository: `${this.capitalize(this.resource)}Repository`
} as ApiNamingSystem<R>;
}
private capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}
// ๐งช Usage examples
const userApi = new ApiGenerator("user");
const postApi = new ApiGenerator("post");
// โ
Generate complete API structures
const userApiStructure = userApi.generateApiStructure();
const postApiStructure = postApi.generateApiStructure();
console.log('๐ค User API Structure:', userApiStructure);
console.log('๐ Post API Structure:', postApiStructure);
// ๐ฏ Type-safe API client
class TypeSafeApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
// ๐ Generate typed API methods
createResource<R extends ApiResource>(resource: R) {
const generator = new ApiGenerator(resource);
return {
[generator.methodName("get")]: () => this.request("GET", generator.endpoint("get")),
[generator.methodName("post")]: (data: any) => this.request("POST", generator.endpoint("post"), data),
[generator.methodName("put")]: (id: string, data: any) => this.request("PUT", `${generator.endpoint("put")}/${id}`, data),
[generator.methodName("delete")]: (id: string) => this.request("DELETE", `${generator.endpoint("delete")}/${id}`)
};
}
private async request(method: string, endpoint: string, data?: any): Promise<any> {
const url = `${this.baseUrl}${endpoint}`;
console.log(`๐ ${method} ${url}`, data ? `with data: ${JSON.stringify(data)}` : '');
// Mock API call
return { success: true, method, endpoint, data };
}
}
// ๐ฎ Usage
const apiClient = new TypeSafeApiClient('https://api.example.com');
const userMethods = apiClient.createResource('user');
const postMethods = apiClient.createResource('post');
// โ
All method names are type-safe and follow naming conventions
userMethods.getUser();
userMethods.postUser({ name: 'Alice', email: '[email protected]' });
postMethods.getPost();
postMethods.deletePost('123');
console.log('๐ Type-safe API client with consistent naming!');
๐ Advanced Techniques
๐งโโ๏ธ Combining Intrinsic Types
// ๐จ Multi-step transformations
type ProcessString<T extends string> = Capitalize<Lowercase<T>>;
type CleanString = ProcessString<"HELLO WORLD">; // "Hello world"
// ๐ฏ Complex naming patterns
type CreateEventHandler<T extends string> = `on${Capitalize<Lowercase<T>>}`;
type ButtonEvents = CreateEventHandler<"CLICK" | "HOVER" | "FOCUS">;
// Result: "onClick" | "onHover" | "onFocus"
// ๐ Nested transformations
type ApiConstant<T extends string> = `API_${Uppercase<T>}_ENDPOINT`;
type UserEndpoint = ApiConstant<"user">; // "API_USER_ENDPOINT"
// โจ Conditional transformations
type SmartCapitalize<T extends string> =
T extends Uppercase<T>
? Capitalize<Lowercase<T>>
: Capitalize<T>;
type SmartTitle1 = SmartCapitalize<"HELLO WORLD">; // "Hello world"
type SmartTitle2 = SmartCapitalize<"hello world">; // "Hello world"
๐๏ธ Building Complex String Utilities
// ๐ฏ String transformation pipeline
type StringPipeline<T extends string> = {
upper: Uppercase<T>;
lower: Lowercase<T>;
capital: Capitalize<T>;
uncapital: Uncapitalize<T>;
properCase: Capitalize<Lowercase<T>>;
constantCase: Uppercase<T>;
};
// ๐งช Testing the pipeline
type TestPipeline = StringPipeline<"hello WORLD">;
// Result: {
// upper: "HELLO WORLD";
// lower: "hello world";
// capital: "Hello WORLD";
// uncapital: "hello WORLD";
// properCase: "Hello world";
// constantCase: "HELLO WORLD";
// }
// ๐จ Environment variable system
type EnvVarTransformer<T extends string> = {
key: Uppercase<T>;
defaultValue: `DEFAULT_${Uppercase<T>}`;
getter: `get${Capitalize<Lowercase<T>>}`;
setter: `set${Capitalize<Lowercase<T>>}`;
validator: `validate${Capitalize<Lowercase<T>>}`;
};
type DatabaseConfig = EnvVarTransformer<"database_url">;
// Result: {
// key: "DATABASE_URL";
// defaultValue: "DEFAULT_DATABASE_URL";
// getter: "getDatabaseUrl";
// setter: "setDatabaseUrl";
// validator: "validateDatabaseUrl";
// }
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Non-String Types
// โ Intrinsic types only work with string types
type BadTransform = Uppercase<number>; // Error!
type AlsoBad = Capitalize<boolean>; // Error!
// โ
Ensure you're working with strings
type GoodTransform<T extends string> = Uppercase<T>;
type AlsoGood<T> = T extends string ? Capitalize<T> : never;
๐คฏ Pitfall 2: Template Literal Complexity
// โ Overly complex template literals can break
type TooComplex = `${Uppercase<string>}_${Lowercase<string>}_${Capitalize<string>}`;
// This creates a very broad type
// โ
Use specific string literals
type JustRight<T extends string> = `${Uppercase<T>}_CONFIG`;
type ConfigName = JustRight<"database">; // "DATABASE_CONFIG"
๐ Pitfall 3: Case Sensitivity
// โ Remember these are case transformations, not comparisons
type CaseComparison = "Hello" extends Uppercase<"hello"> ? true : false; // false
// Because "Hello" !== "HELLO"
// โ
Use proper case transformations
type ProperCheck = Uppercase<"hello"> extends "HELLO" ? true : false; // true
๐ ๏ธ Best Practices
- ๐ฏ Use Specific String Literals: Avoid broad
string
types when possible - ๐ Combine Thoughtfully: Chain intrinsic types for complex transformations
- ๐ก๏ธ Add Type Constraints: Use
extends string
to ensure string types - ๐จ Create Utility Types: Build reusable transformation patterns
- โจ Document Transformations: Complex chains can be hard to follow
- ๐ Test Edge Cases: Consider empty strings and special characters
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Type-Safe Internationalization System
Create a sophisticated i18n system using intrinsic string types:
๐ Requirements:
- โ Generate translation keys with proper casing
- ๐ง Create locale-specific type definitions
- ๐จ Build pluralization support with type safety
- ๐ Handle nested translation paths
- โจ Type-safe parameter interpolation!
๐ Bonus Points:
- Add currency formatting types
- Create date/time formatting types
- Build RTL language support types
๐ก Solution
๐ Click to see solution
// ๐ฏ Base translation structure
type TranslationKey =
| "welcome_message"
| "user_login"
| "password_reset"
| "email_verification"
| "account_settings"
| "profile_update";
// ๐ Locale definitions
type Locale = "en" | "es" | "fr" | "de" | "ja" | "zh";
// ๐จ Translation key transformations
type I18nKeyTransforms<T extends string> = {
// Camel case for JavaScript
camelCase: Uncapitalize<Capitalize<T>>;
// Constant case for constants
constantCase: Uppercase<T>;
// Kebab case for URLs
kebabCase: Lowercase<T>;
// Pascal case for components
pascalCase: Capitalize<T>;
// Dot notation for nested keys
dotNotation: `i18n.${Lowercase<T>}`;
// Bracket notation for dynamic access
bracketNotation: `['${Lowercase<T>}']`;
};
// โจ Generate locale-specific keys
type LocaleKey<L extends Locale, K extends string> = `${Uppercase<L>}_${Uppercase<K>}`;
// ๐งช Pluralization support
type PluralForm = "zero" | "one" | "two" | "few" | "many" | "other";
type PluralKey<K extends string, P extends PluralForm> = `${Lowercase<K>}.${P}`;
// ๐ฎ Parameter interpolation
type InterpolationParam = `{{${string}}}`;
type TranslationValue = string | `${string}${InterpolationParam}${string}`;
// ๐ฏ Complete i18n type system
type I18nSystem<L extends Locale> = {
// Locale information
locale: L;
localeUpper: Uppercase<L>;
localeLower: Lowercase<L>;
localeCapital: Capitalize<L>;
// Translation keys
keys: {
[K in TranslationKey as Lowercase<K>]: string;
};
// Pluralized keys
plurals: {
[K in TranslationKey as PluralKey<K, PluralForm>]: string;
};
// Nested translations
nested: {
[K in TranslationKey as `${Lowercase<K>}.title`]: string;
} & {
[K in TranslationKey as `${Lowercase<K>}.description`]: string;
};
// Form-specific translations
forms: {
[K in TranslationKey as `form.${Lowercase<K>}.label`]: string;
} & {
[K in TranslationKey as `form.${Lowercase<K>}.placeholder`]: string;
} & {
[K in TranslationKey as `form.${Lowercase<K>}.error`]: string;
};
};
// ๐ I18n manager class
class I18nManager<L extends Locale> {
private translations: Map<string, string> = new Map();
private locale: L;
constructor(locale: L) {
this.locale = locale;
}
// ๐ฏ Add translation
addTranslation<K extends TranslationKey>(
key: K,
value: string,
params?: Record<string, string>
): void {
const transformedKey = this.transformKey(key);
let processedValue = value;
// Handle parameter interpolation
if (params) {
Object.entries(params).forEach(([param, replacement]) => {
processedValue = processedValue.replace(
new RegExp(`{{${param}}}`, 'g'),
replacement
);
});
}
this.translations.set(transformedKey, processedValue);
}
// ๐จ Get translation
getTranslation<K extends TranslationKey>(
key: K,
params?: Record<string, string>
): string {
const transformedKey = this.transformKey(key);
let translation = this.translations.get(transformedKey) || key;
// Handle parameter interpolation
if (params) {
Object.entries(params).forEach(([param, replacement]) => {
translation = translation.replace(
new RegExp(`{{${param}}}`, 'g'),
replacement
);
});
}
return translation;
}
// ๐ Get plural translation
getPluralTranslation<K extends TranslationKey>(
key: K,
count: number,
params?: Record<string, string>
): string {
const pluralForm = this.getPluralForm(count);
const pluralKey = `${key.toLowerCase()}.${pluralForm}`;
return this.getTranslation(pluralKey as K, params);
}
// โจ Get nested translation
getNestedTranslation<K extends TranslationKey>(
key: K,
subKey: "title" | "description",
params?: Record<string, string>
): string {
const nestedKey = `${key.toLowerCase()}.${subKey}`;
return this.getTranslation(nestedKey as K, params);
}
// ๐งช Get form translation
getFormTranslation<K extends TranslationKey>(
key: K,
formType: "label" | "placeholder" | "error",
params?: Record<string, string>
): string {
const formKey = `form.${key.toLowerCase()}.${formType}`;
return this.getTranslation(formKey as K, params);
}
// ๐ฎ Generate all key variations
generateKeyVariations<K extends TranslationKey>(key: K): I18nKeyTransforms<K> {
return {
camelCase: this.toCamelCase(key) as Uncapitalize<Capitalize<K>>,
constantCase: key.toUpperCase() as Uppercase<K>,
kebabCase: key.toLowerCase() as Lowercase<K>,
pascalCase: this.toPascalCase(key) as Capitalize<K>,
dotNotation: `i18n.${key.toLowerCase()}` as `i18n.${Lowercase<K>}`,
bracketNotation: `['${key.toLowerCase()}']` as `['${Lowercase<K>}']`
};
}
// ๐ง Transform key based on locale
private transformKey(key: string): string {
return `${this.locale.toUpperCase()}_${key.toUpperCase()}`;
}
// ๐ฏ Determine plural form (simplified)
private getPluralForm(count: number): PluralForm {
if (count === 0) return "zero";
if (count === 1) return "one";
if (count === 2) return "two";
if (count > 2 && count < 5) return "few";
if (count >= 5) return "many";
return "other";
}
// ๐จ Helper methods
private toCamelCase(str: string): string {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
private toPascalCase(str: string): string {
return this.toCamelCase(str).replace(/^[a-z]/, match => match.toUpperCase());
}
}
// ๐งช Usage examples
const englishI18n = new I18nManager("en");
const spanishI18n = new I18nManager("es");
// โ
Add translations
englishI18n.addTranslation("welcome_message", "Welcome to our app, {{name}}!");
englishI18n.addTranslation("user_login", "User Login");
englishI18n.addTranslation("user_login.one", "One user logged in");
englishI18n.addTranslation("user_login.many", "{{count}} users logged in");
spanishI18n.addTranslation("welcome_message", "ยกBienvenido a nuestra app, {{name}}!");
spanishI18n.addTranslation("user_login", "Iniciar Sesiรณn");
// ๐ฏ Get translations
const welcomeEn = englishI18n.getTranslation("welcome_message", { name: "Alice" });
const welcomeEs = spanishI18n.getTranslation("welcome_message", { name: "Alice" });
console.log('๐บ๐ธ English:', welcomeEn);
console.log('๐ช๐ธ Spanish:', welcomeEs);
// ๐ Generate key variations
const keyVariations = englishI18n.generateKeyVariations("user_login");
console.log('๐ง Key variations:', keyVariations);
// โจ Plural translations
const pluralTranslation = englishI18n.getPluralTranslation("user_login", 5, { count: "5" });
console.log('๐ Plural translation:', pluralTranslation);
// ๐ฎ Type-safe translation builder
type TranslationBuilder<L extends Locale> = {
[K in TranslationKey as `${Lowercase<L>}${Capitalize<Lowercase<K>>}`]: (params?: Record<string, string>) => string;
};
class TypeSafeTranslationBuilder<L extends Locale> {
constructor(private i18nManager: I18nManager<L>) {}
// ๐ฏ Build type-safe translation methods
build(): TranslationBuilder<L> {
const builder = {} as any;
const keys: TranslationKey[] = [
"welcome_message",
"user_login",
"password_reset",
"email_verification",
"account_settings",
"profile_update"
];
keys.forEach(key => {
const methodName = `${this.i18nManager['locale'].toLowerCase()}${this.capitalize(key.toLowerCase())}`;
builder[methodName] = (params?: Record<string, string>) => {
return this.i18nManager.getTranslation(key, params);
};
});
return builder;
}
private capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1).replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
}
// ๐งช Usage
const englishBuilder = new TypeSafeTranslationBuilder(englishI18n);
const englishTranslations = englishBuilder.build();
// โ
Type-safe method calls
const welcomeTranslation = englishTranslations.enWelcomeMessage({ name: "Alice" });
const loginTranslation = englishTranslations.enUserLogin();
console.log('๐ Type-safe i18n system with intrinsic string types!');
console.log('๐ค Welcome:', welcomeTranslation);
console.log('๐ Login:', loginTranslation);
๐ Key Takeaways
Youโve mastered intrinsic string manipulation types! Hereโs what you can now do:
- โ Transform strings with built-in precision using Uppercase, Lowercase, Capitalize, and Uncapitalize ๐ช
- โ Build consistent naming conventions across your entire codebase ๐ก๏ธ
- โ Create type-safe string utilities that work at compile-time ๐ฏ
- โ Design elegant API and CSS systems with perfect string transformations ๐
- โ Combine transformations for sophisticated string manipulation ๐
Remember: Intrinsic string types are like having a Swiss Army knife ๐ง for string transformations at the type level!
๐ค Next Steps
Congratulations! ๐ Youโve mastered intrinsic string manipulation types!
Hereโs what to explore next:
- ๐ป Practice with the i18n exercise above - try different locales and translations
- ๐๏ธ Build your own string transformation utilities for real projects
- ๐ Move on to our next tutorial: Recursive Types: Self-Referential Type Definitions
- ๐ Share your string manipulation patterns with the TypeScript community!
You now possess TypeScriptโs most convenient string transformation tools. Use them to create consistent, type-safe naming systems that make your code more maintainable and elegant. Remember - every string transformation master started with curiosity about case conversion. Keep experimenting, keep building, and most importantly, have fun transforming strings at the type level! ๐โจ
Happy string transforming! ๐๐คโจ