Item 35: Prefer More Precise Alternatives to String Types
May 10, 2024 ยท View on GitHub
Things to Remember
- Avoid "stringly typed" code. Prefer more appropriate types where not every
stringis a possibility. - Prefer a union of string literal types to
stringif that more accurately describes the domain of a variable. You'll get stricter type checking and improve the development experience. - Prefer
keyof Ttostringfor function parameters that are expected to be properties of an object.
Code Samples
interface Album {
artist: string;
title: string;
releaseDate: string; // YYYY-MM-DD
recordingType: string; // E.g., "live" or "studio"
}
const kindOfBlue: Album = {
artist: 'Miles Davis',
title: 'Kind of Blue',
releaseDate: 'August 17th, 1959', // Oops!
recordingType: 'Studio', // Oops!
}; // OK
function recordRelease(title: string, date: string) { /* ... */ }
recordRelease(kindOfBlue.releaseDate, kindOfBlue.title); // OK, should be error
type RecordingType = 'studio' | 'live';
interface Album {
artist: string;
title: string;
releaseDate: Date;
recordingType: RecordingType;
}
const kindOfBlue: Album = {
artist: 'Miles Davis',
title: 'Kind of Blue',
releaseDate: new Date('1959-08-17'),
recordingType: 'Studio'
// ~~~~~~~~~~~~ Type '"Studio"' is not assignable to type 'RecordingType'
};
function getAlbumsOfType(recordingType: string): Album[] {
// ...
}
/** What type of environment was this recording made in? */
type RecordingType = 'live' | 'studio';
function pluck(records, key) {
return records.map(r => r[key]);
}
function pluck(records: any[], key: string): any[] {
return records.map(r => r[key]);
}
function pluck<T>(records: T[], key: string): any[] {
return records.map(r => r[key]);
// ~~~~~~ Element implicitly has an 'any' type
// because type '{}' has no index signature
}
type K = keyof Album;
// ^? type K = keyof Album
// (equivalent to "artist" | "title" | "releaseDate" | "recordingType")
function pluck<T>(records: T[], key: keyof T) {
return records.map(r => r[key]);
}
const releaseDates = pluck(albums, 'releaseDate');
// ^? const releaseDates: (string | Date)[]
function pluck<T, K extends keyof T>(records: T[], key: K): T[K][] {
return records.map(r => r[key]);
}
const dates = pluck(albums, 'releaseDate');
// ^? const dates: Date[]
const artists = pluck(albums, 'artist');
// ^? const artists: string[]
const types = pluck(albums, 'recordingType');
// ^? const types: RecordingType[]
const mix = pluck(albums, Math.random() < 0.5 ? 'releaseDate' : 'artist');
// ^? const mix: (string | Date)[]
const badDates = pluck(albums, 'recordingDate');
// ~~~~~~~~~~~~~~~
// Argument of type '"recordingDate"' is not assignable to parameter of type ...