We continue to publish a series of adapted and supplemented translations " TypeScript
. "
Other parts:
- Part 1. Basics
- Part 2. Types for every day
- Part 3. Narrowing types
- Part 4. More about functions
- Part 5. Object types
The type system TS
allows you to create types based on other types.
(generics). . , .
identity
, :
function identity(arg: number): number { return arg }
, , any
:
function identity(arg: any): any { return arg }
, .
- . , , , :
function identity<Type>(arg: Type): Type { return arg }
Type
, .
(), .
. , :
const output = identity<string>('myStr') // let output: string
.
:
const output = identity('myStr') // let output: string
. , , .
arg
?
function loggingIdentity<Type>(arg: Type): Type { console.log(arg.length) // Property 'length' does not exist on type 'Type'. // 'length' 'Type' return arg }
, (, , ) , , arg
length
, , .
, Type
:
function loggingIdentity<Type>(arg: Type[]): Type[] { console.log(arg.length) return arg }
, Type
arg
, Type
, Type
. , .
:
function loggingIdentity<Type>(arg: Array<Type>): Array<Type> { console.log(arg.length) return arg }
(-) , :
function identity<Type>(arg: Type): Type { return arg } const myIdentity: <Type>(arg: Type) => Type = identity
:
function identity<Type>(arg: Type): Type { return arg } const myIdentity: <Input>(arg: Input) => Input = identity
:
function identity<Type>(arg: Type): Type { return arg } const myIdentity: { <Type>(arg: Type): Type } = identity
:
interface GenericIdentityFn { <Type>(arg: Type): Type } function identity<Type>(arg: Type): Type { return arg } const myIdentity: GenericIdentityFn = identity
, , :
interface GenericIdentityFn<Type> { (arg: Type): Type } function identity<Type>(arg: Type): Type { return arg } const myIdentity: GenericIdentityFn<number> = identity
, .
, (enums) (namespaces) .
, :
class GenericNumber<NumType> { zeroValue: NumType add: (x: NumType, y: NumType) => NumType } const myGenericNum = new GenericNumber<number>() myGenericNum.zeroValue = 0 myGenericNum.add = (x, y) => x + y
. :
const stringNumeric = new GenericNumber<string>() stringNumeric.zeroValue = '' stringNumeric.add = (x, y) => x + y console.log(stringNumeric.add(stringNumeric.zeroValue, 'test'))
: . . , .
, , , . loggingIdentity
length
arg
, , , :
function loggingIdentity<Type>(arg: Type): Type { console.log(arg.length) // Property 'length' does not exist on type 'Type'. return arg }
, , length
. .
, . length
extends
:
interface Lengthwise { length: number } function loggingIdentity<Type extends Lengthwise>(arg: Type): Type { console.log(arg.length) // `length` return arg }
, :
loggingIdentity(3) // Argument of type 'number' is not assignable to parameter of type 'Lengthwise'. // 'number' 'Lengthwise'
, :
loggingIdentity({ length: 10, value: 3 })
, . . , , . :
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) { return obj[key] } const x = { a: 1, b: 2, c: 3, d: 4 } getProperty(x, 'a') getProperty(x, 'm') // Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.
, -. :
function create<Type>(c: { new (): Type }): Type { return new c() }
prototype
- :
class BeeKeeper { hasMask: boolean } class ZooKeeper { nametag: string } class Animal { numLegs: number } class Bee extends Animal { keeper: BeeKeeper } class Lion extends Animal { keeper: ZooKeeper } function createInstance<A extends Animal>(c: new () => A): A { return new c() } createInstance(Lion).keeper.nametag createInstance(Bee).keeper.hasMask
.
keyof
keyof
"" :
type Point = { x: number, y: number } type P = keyof Point // type P = keyof Point
(index signature) string
number
, keyof
:
type Arrayish = { [n: number]: unknown } type A = keyof Arrayish // type A = number type Mapish = { [k: string]: boolean } type M = keyof Mapish // type M = string | number
, M
string | number
. , JS
, obj[0]
— , obj['0']
.
keyof
(mapped types), .
typeof
JS
typeof
, :
console.log(typeof ', !') // string
TS
typeof
:
const s = '' const n: typeof s // const n: string
typeof
. , ReturnType<T>
. :
type Predicate = (x: unknown) => boolean type K = ReturnType<Predicate> // type K = boolean
ReturnType
, :
function f() { return { x: 10, y: 3 } } type P = ReturnType<f> // 'f' refers to a value, but is being used as a type here. Did you mean 'typeof f'? // 'f' , . , 'typeof f'
: — . f
typeof
:
function f() { return { x: 10, y: 3 } } type P = ReturnType<typeof f> // type P = { x: number, y: number }
TS
, typeof
.
typeof
( ) . , :
// ReturnType<typeof msgbox>, const shouldContinue: typeof msgbox(' , ?') // ',' expected
(indexed access types)
:
type Person = { age: number, name: string, alive: boolean } type Age = Person['age'] // type Age = number
— , , keyof
:
type I1 = Person['age' | 'name'] // type I1 = string | number type I2 = Person[keyof Person] // type I2 = string | number | boolean type AliveOrName = 'alive' | 'name' type I3 = Person[AliveOrName] // type I3 = string | boolean
:
type I1 = Person['alve'] // Property 'alve' does not exist on type 'Person'.
number
. typeof
:
const MyArray = [ { name: 'Alice', age: 15 }, { name: 'Bob', age: 23 }, { name: 'John', age: 38 }, ] type Person = typeof MyArray[number] type Person = { name: string age: number } type Age = typeof MyArray[number]['age'] type Age = number // type Age2 = Person['age'] type Age2 = number
, const
, :
const key = 'age' type Age = Person[key] /* Type 'any' cannot be used as an index type. 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'? */ /* 'any' . 'key' , . , 'typeof key' */
, (type alias):
type key = 'age' type Age = Person[key]
(conditional types)
, . TS
. .
interface Animal { live(): void } interface Dog extends Animal { woof(): void } type Example1 = Dog extends Animal ? number : string // type Example1 = number type Example2 = RegExp extends Animal ? number : string // type Example2 = string
, JS
( ? :
).
SomeType extends OtherType ? TrueType : FalseType
extends
extends
, (), , ().
. ( ).
:
interface IdLabel { id: number /* */ } interface NameLabel { name: string /* */ } function createLabel(id: number): IdLabel function createLabel(name: string): NameLabel function createLabel(nameOrId: string | number): IdLabel | NameLabel function createLabel(nameOrId: string | number): IdLabel | NameLabel { throw ' ' }
createLabel
, .
:
- , .
- 3 : , (
string
number
), (string
number
). .
, :
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel
:
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> { throw ' ' } let a = createLabel('typescript') // let a: NameLabel let b = createLabel(2.8) // let b: IdLabel let c = createLabel(Math.random() ? 'hello' : 42) // let c: NameLabel | IdLabel
. , (type guards) , , .
:
type MessageOf<T> = T['message'] // Type '"message"' cannot be used to index type 'T'. // '"message"' 'T'
, TS
T
message
. T
, TS
"":
type MessageOf<T extends { message: unknown }> = T['message'] interface Email { message: string } interface Dog { bark(): void } type EmailMessageContents = MessageOf<Email> // type EmailMessageContents = string
, MessageOf
, "" never
? "" :
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never interface Email { message: string } interface Dog { bark(): void } type EmailMessageContents = MessageOf<Email> // type EmailMessageContents = string type DogMessageContents = MessageOf<Dog> // type DogMessageContents = never
, TS
, T
message
.
Flatten
, , :
type Flatten<T> = T extends any[] ? T[number] : T // type Str = Flatten<string[]> // type Str = string // type Num = Flatten<number> // type Num = number
Flatten
, number
string[]
. , .
. , .
infer
. , Flatten
:
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type
infer
Item
T
. "" , .
(type aliases) infer
. , :
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never type Num = GetReturnType<() => number> // type Num = number type Str = GetReturnType<(x: string) => string> // type Str = string type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]> // type Bools = boolean[]
( ), . .
declare function stringOrNum(x: string): number declare function stringOrNum(x: number): string declare function stringOrNum(x: string | number): string | number type T1 = ReturnType<typeof stringOrNum> // type T1 = string | number
(distributive conditional types)
, (union). :
type ToArray<Type> = Type extends any ? Type[] : never
ToArray
, .
type ToArray<Type> = Type extends any ? Type[] : never type StrArrOrNumArr = ToArray<string | number> // type StrArrOrNumArr = string[] | number[]
StrOrNumArray
:
string | number
:
ToArray<string> | ToArray<number>
:
string[] | number[]
, . extends
:
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never // 'StrOrNumArr' type StrOrNumArr = ToArrayNonDist<string | number> // type StrOrNumArr = (string | number)[]
(mapped types)
, , :
type OnlyBoolsAndHorses = { [key: string]: boolean | Horse } const conforms: OnlyBoolsAndHorses = { del: true, rodney: false, }
— , , keyof
, :
type OptionsFlags<Type> = { [Property in keyof Type]: boolean }
OptionsFlag
Type
boolean
.
type FeatureFlags = { darkMode: () => void newUserProfile: () => void } type FeatureOptions = OptionsFlags<FeatureFlags> // type FeatureOptions = { darkMode: boolean, newUserProfile: boolean }
(mapping modifiers)
, : readonly
?
, () , .
-
+
. , +
.
// `readonly` type CreateMutable<Type> = { -readonly [Property in keyof Type]: Type[Property] } type LockedAccount = { readonly id: string readonly name: string } type UnlockedAccount = CreateMutable<LockedAccount> // type UnlockedAccount = { id: string, name: string }
// `optional` type Concrete<Type> = { [Property in keyof Type]-?: Type[Property] } type MaybeUser = { id: string name?: string age?: number } type User = Concrete<MaybeUser> // type User = { id: string, name: string, age: number }
as
TS
4.1 , as
:
type MappedTypeWithNewProperties<Type> = { [Properties in keyof Type as NewKeyType]: Type[Properties] }
, (. ):
type Getters<Type> = { [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property] } interface Person { name: string age: number location: string } type LazyPerson = Getters<Person> // type LazyPerson = { getName: () => string, getAge: () => number, getLocation: () => string }
never
:
// `kind` type RemoveKindField<Type> = { [Property in keyof Type as Exclude<Property, 'kind'>]: Type[Property] } interface Circle { kind: 'circle' radius: number } type KindlessCircle = RemoveKindField<Circle> // type KindlessCircle = { radius: number }
, , . true
false
, pii
true
:
type ExtractPII<Type> = { [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false } type DBFields = { id: { format: 'incrementing' } name: { type: string, pii: true } } type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields> // type ObjectsNeedingGDPRDeletion = { id: false, name: true }
(template literal types)
.
, JS
, . , :
type World = 'world' type Greeting = `hello ${World}` // type Greeting = 'hello world'
, , :
type EmailLocaleIDs = 'welcome_email' | 'email_heading' type FooterLocaleIDs = 'footer_title' | 'footer_sendoff' type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id` /* type AllLocaleIDs = 'welcome_email_id' | 'email_heading_id' | 'footer_title_id' | 'footer_sendoff_id' */
:
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id` type Lang = 'en' | 'ja' | 'pt' type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}` /* type LocaleMessageIDs = 'en_welcome_email_id' | 'en_email_heading_id' | 'en_footer_title_id' | 'en_footer_sendoff_id' | 'ja_welcome_email_id' | 'ja_email_heading_id' | 'ja_footer_title_id' | 'ja_footer_sendoff_id' | 'pt_welcome_email_id' | 'pt_email_heading_id' | 'pt_footer_title_id' | 'pt_footer_sendoff_id' */
, .
.
, JS
. , on
, :
const person = makeWatchedObject({ firstName: 'John', lastName: 'Smith', age: 30, }) person.on('firstNameChanged', (newValue) => { console.log(` ${newValue}!`) })
, on
firstNameChanged
, firstName
.
:
type PropEventSource<Type> = { on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void } // " " `on`, // declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>
:
const person = makeWatchedObject({ firstName: 'John', lastName: 'Smith', age: 26 }) person.on('firstNameChanged', () => {}) person.on('firstName', () => {}) // Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'. // '"firstName"' ... person.on('frstNameChanged', () => {}) // Argument of type '"firstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.
, . any
. .
, eventName
:
type PropEventSource<Type> = { on<Key extends string & keyof Type> // (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void } declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type> const person = makeWatchedObject({ firstName: 'Jane', lastName: 'Air', age: 26 }) person.on('firstNameChanged', newName => { // (parameter) newName: string console.log(` - ${newName.toUpperCase()}`) }) person.on('ageChanged', newAge => { // (parameter) newAge: number if (newAge < 0) { console.warn('! ') } })
on
.
firstNameChanged
, TS
Key
. TS
Key
"", Changed
, firstName
. on
firstName
, string
. ageChanged
, TS
age
, number
.
(intrisic string manipulation types)
TS
, . .d.ts
, TS
.
Uppercase<StringType>
—
type Greeting = 'Hello, world' type ShoutyGreeting = Uppercase<Greeting> // type ShoutyGreeting = 'HELLO, WORLD' type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}` type MainID = ASCIICacheKey<'my_app'> // type MainID = 'ID-MY_APP'
Lowercase<StringType>
—
type Greeting = 'Hello, world' type QuietGreeting = Lowercase<Greeting> // type QuietGreeting = 'hello, world' type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}` type MainID = ASCIICacheKey<'MY_APP'> // type MainID = 'id-my_app'
Capitilize<StringType>
—
type LowercaseGreeting = 'hello, world' type Greeting = Capitalize<LowercaseGreeting> // type Greeting = 'Hello, world'
Uncapitilize<StringType>
—
type UppercaseGreeting = 'HELLO WORLD' type UncomfortableGreeting = Uncapitalize<UppercaseGreeting> // type UncomfortableGreeting = 'hELLO WORLD'
:
function applyStringMapping(symbol: Symbol, str: string) { switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { case IntrinsicTypeKind.Uppercase: return str.toUpperCase() case IntrinsicTypeKind.Lowercase: return str.toLowerCase() case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1) case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1) } return str }
10% !