typescript-advanced-types

安装量: 15.7K
排名: #147

安装

npx skills add https://github.com/wshobson/agents --skill typescript-advanced-types
TypeScript Advanced Types
Comprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.
When to Use This Skill
Building type-safe libraries or frameworks
Creating reusable generic components
Implementing complex type inference logic
Designing type-safe API clients
Building form validation systems
Creating strongly-typed configuration objects
Implementing type-safe state management
Migrating JavaScript codebases to TypeScript
Core Concepts
1. Generics
Purpose:
Create reusable, type-flexible components while maintaining type safety.
Basic Generic Function:
function
identity
<
T
>
(
value
:
T
)
:
T
{
return
value
;
}
const
num
=
identity
<
number
>
(
42
)
;
// Type: number
const
str
=
identity
<
string
>
(
"hello"
)
;
// Type: string
const
auto
=
identity
(
true
)
;
// Type inferred: boolean
Generic Constraints:
interface
HasLength
{
length
:
number
;
}
function
logLength
<
T
extends
HasLength
>
(
item
:
T
)
:
T
{
console
.
log
(
item
.
length
)
;
return
item
;
}
logLength
(
"hello"
)
;
// OK: string has length
logLength
(
[
1
,
2
,
3
]
)
;
// OK: array has length
logLength
(
{
length
:
10
}
)
;
// OK: object has length
// logLength(42); // Error: number has no length
Multiple Type Parameters:
function
merge
<
T
,
U
>
(
obj1
:
T
,
obj2
:
U
)
:
T
&
U
{
return
{
...
obj1
,
...
obj2
}
;
}
const
merged
=
merge
(
{
name
:
"John"
}
,
{
age
:
30
}
)
;
// Type: { name: string } & { age: number }
2. Conditional Types
Purpose:
Create types that depend on conditions, enabling sophisticated type logic.
Basic Conditional Type:
type
IsString
<
T
>
=
T
extends
string
?
true
:
false
;
type
A
=
IsString
<
string
>
;
// true
type
B
=
IsString
<
number
>
;
// false
Extracting Return Types:
type
ReturnType
<
T
>
=
T
extends
(
...
args
:
any
[
]
)
=>
infer
R
?
R
:
never
;
function
getUser
(
)
{
return
{
id
:
1
,
name
:
"John"
}
;
}
type
User
=
ReturnType
<
typeof
getUser
>
;
// Type:
Distributive Conditional Types:
type
ToArray
<
T
>
=
T
extends
any
?
T
[
]
:
never
;
type
StrOrNumArray
=
ToArray
<
string
|
number
>
;
// Type: string[] | number[]
Nested Conditions:
type
TypeName
<
T
>
=
T
extends
string
?
"string"
:
T
extends
number
?
"number"
:
T
extends
boolean
?
"boolean"
:
T
extends
undefined
?
"undefined"
:
T
extends
Function
?
"function"
:
"object"
;
type
T1
=
TypeName
<
string
>
;
// "string"
type
T2
=
TypeName
<
(
)
=>
void
>
;
// "function"
3. Mapped Types
Purpose:
Transform existing types by iterating over their properties.
Basic Mapped Type:
type
Readonly
<
T
>
=
{
readonly
[
P
in
keyof
T
]
:
T
[
P
]
;
}
;
interface
User
{
id
:
number
;
name
:
string
;
}
type
ReadonlyUser
=
Readonly
<
User
>
;
// Type:
Optional Properties:
type
Partial
<
T
>
=
{
[
P
in
keyof
T
]
?
:
T
[
P
]
;
}
;
type
PartialUser
=
Partial
<
User
>
;
// Type:
Key Remapping:
type
Getters
<
T
>
=
{
[
K
in
keyof
T
as
`
get
${
Capitalize
<
string
&
K
>
}
`
]
:
(
)
=>
T
[
K
]
;
}
;
interface
Person
{
name
:
string
;
age
:
number
;
}
type
PersonGetters
=
Getters
<
Person
>
;
// Type:
Filtering Properties:
type
PickByType
<
T
,
U
>
=
{
[
K
in
keyof
T
as
T
[
K
]
extends
U
?
K
:
never
]
:
T
[
K
]
;
}
;
interface
Mixed
{
id
:
number
;
name
:
string
;
age
:
number
;
active
:
boolean
;
}
type
OnlyNumbers
=
PickByType
<
Mixed
,
number
>
;
// Type:
4. Template Literal Types
Purpose:
Create string-based types with pattern matching and transformation.
Basic Template Literal:
type
EventName
=
"click"
|
"focus"
|
"blur"
;
type
EventHandler
=
`
on
${
Capitalize
<
EventName
>
}
`
;
// Type: "onClick" | "onFocus" | "onBlur"
String Manipulation:
type
UppercaseGreeting
=
Uppercase
<
"hello"
>
;
// "HELLO"
type
LowercaseGreeting
=
Lowercase
<
"HELLO"
>
;
// "hello"
type
CapitalizedName
=
Capitalize
<
"john"
>
;
// "John"
type
UncapitalizedName
=
Uncapitalize
<
"John"
>
;
// "john"
Path Building:
type
Path
<
T
>
=
T
extends
object
?
{
[
K
in
keyof
T
]
:
K
extends
string
?
`
${
K
}
`
|
`
${
K
}
.
${
Path
<
T
[
K
]
>
}
`
:
never
;
}
[
keyof
T
]
:
never
;
interface
Config
{
server
:
{
host
:
string
;
port
:
number
;
}
;
database
:
{
url
:
string
;
}
;
}
type
ConfigPath
=
Path
<
Config
>
;
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
5. Utility Types
Built-in Utility Types:
// Partial - Make all properties optional
type
PartialUser
=
Partial
<
User
>
;
// Required - Make all properties required
type
RequiredUser
=
Required
<
PartialUser
>
;
// Readonly - Make all properties readonly
type
ReadonlyUser
=
Readonly
<
User
>
;
// Pick - Select specific properties
type
UserName
=
Pick
<
User
,
"name"
|
"email"
>
;
// Omit - Remove specific properties
type
UserWithoutPassword
=
Omit
<
User
,
"password"
>
;
// Exclude - Exclude types from union
type
T1
=
Exclude
<
"a"
|
"b"
|
"c"
,
"a"
>
;
// "b" | "c"
// Extract - Extract types from union
type
T2
=
Extract
<
"a"
|
"b"
|
"c"
,
"a"
|
"b"
>
;
// "a" | "b"
// NonNullable - Exclude null and undefined
type
T3
=
NonNullable
<
string
|
null
|
undefined
>
;
// string
// Record - Create object type with keys K and values T
type
PageInfo
=
Record
<
"home"
|
"about"
,
{
title
:
string
}
>
;
Advanced Patterns
Pattern 1: Type-Safe Event Emitter
type
EventMap
=
{
"user:created"
:
{
id
:
string
;
name
:
string
}
;
"user:updated"
:
{
id
:
string
}
;
"user:deleted"
:
{
id
:
string
}
;
}
;
class
TypedEventEmitter
<
T
extends
Record
<
string
,
any
>>
{
private
listeners
:
{
[
K
in
keyof
T
]
?
:
Array
<
(
data
:
T
[
K
]
)
=>
void
>
;
}
=
{
}
;
on
<
K
extends
keyof
T
>
(
event
:
K
,
callback
:
(
data
:
T
[
K
]
)
=>
void
)
:
void
{
if
(
!
this
.
listeners
[
event
]
)
{
this
.
listeners
[
event
]
=
[
]
;
}
this
.
listeners
[
event
]
!
.
push
(
callback
)
;
}
emit
<
K
extends
keyof
T
>
(
event
:
K
,
data
:
T
[
K
]
)
:
void
{
const
callbacks
=
this
.
listeners
[
event
]
;
if
(
callbacks
)
{
callbacks
.
forEach
(
(
callback
)
=>
callback
(
data
)
)
;
}
}
}
const
emitter
=
new
TypedEventEmitter
<
EventMap
>
(
)
;
emitter
.
on
(
"user:created"
,
(
data
)
=>
{
console
.
log
(
data
.
id
,
data
.
name
)
;
// Type-safe!
}
)
;
emitter
.
emit
(
"user:created"
,
{
id
:
"1"
,
name
:
"John"
}
)
;
// emitter.emit("user:created", { id: "1" }); // Error: missing 'name'
Pattern 2: Type-Safe API Client
type
HTTPMethod
=
"GET"
|
"POST"
|
"PUT"
|
"DELETE"
;
type
EndpointConfig
=
{
"/users"
:
{
GET
:
{
response
:
User
[
]
}
;
POST
:
{
body
:
{
name
:
string
;
email
:
string
}
;
response
:
User
}
;
}
;
"/users/:id"
:
{
GET
:
{
params
:
{
id
:
string
}
;
response
:
User
}
;
PUT
:
{
params
:
{
id
:
string
}
;
body
:
Partial
<
User
>
;
response
:
User
}
;
DELETE
:
{
params
:
{
id
:
string
}
;
response
:
void
}
;
}
;
}
;
type
ExtractParams
<
T
>
=
T
extends
{
params
:
infer
P
}
?
P
:
never
;
type
ExtractBody
<
T
>
=
T
extends
{
body
:
infer
B
}
?
B
:
never
;
type
ExtractResponse
<
T
>
=
T
extends
{
response
:
infer
R
}
?
R
:
never
;
class
APIClient
<
Config
extends
Record
<
string
,
Record
<
HTTPMethod
,
any
>>>
{
async
request
<
Path
extends
keyof
Config
,
Method
extends
keyof
Config
[
Path
]
>
(
path
:
Path
,
method
:
Method
,
...
[
options
]
:
ExtractParams
<
Config
[
Path
]
[
Method
]
>
extends
never
?
ExtractBody
<
Config
[
Path
]
[
Method
]
>
extends
never
?
[
]
:
[
{
body
:
ExtractBody
<
Config
[
Path
]
[
Method
]
>
}
]
:
[
{
params
:
ExtractParams
<
Config
[
Path
]
[
Method
]
>
;
body
?
:
ExtractBody
<
Config
[
Path
]
[
Method
]
>
;
}
,
]
)
:
Promise
<
ExtractResponse
<
Config
[
Path
]
[
Method
]
>>
{
// Implementation here
return
{
}
as
any
;
}
}
const
api
=
new
APIClient
<
EndpointConfig
>
(
)
;
// Type-safe API calls
const
users
=
await
api
.
request
(
"/users"
,
"GET"
)
;
// Type: User[]
const
newUser
=
await
api
.
request
(
"/users"
,
"POST"
,
{
body
:
{
name
:
"John"
,
email
:
"john@example.com"
}
,
}
)
;
// Type: User
const
user
=
await
api
.
request
(
"/users/:id"
,
"GET"
,
{
params
:
{
id
:
"123"
}
,
}
)
;
// Type: User
Pattern 3: Builder Pattern with Type Safety
type
BuilderState
<
T
>
=
{
[
K
in
keyof
T
]
:
T
[
K
]
|
undefined
;
}
;
type
RequiredKeys
<
T
>
=
{
[
K
in
keyof
T
]
-
?
:
{
}
extends
Pick
<
T
,
K
>
?
never
:
K
;
}
[
keyof
T
]
;
type
OptionalKeys
<
T
>
=
{
[
K
in
keyof
T
]
-
?
:
{
}
extends
Pick
<
T
,
K
>
?
K
:
never
;
}
[
keyof
T
]
;
type
IsComplete
<
T
,
S
>
=
RequiredKeys
<
T
>
extends
keyof
S
?
S
[
RequiredKeys
<
T
>
]
extends
undefined
?
false
:
true
:
false
;
class
Builder
<
T
,
S
extends
BuilderState
<
T
>
=
{
}
>
{
private
state
:
S
=
{
}
as
S
;
set
<
K
extends
keyof
T
>
(
key
:
K
,
value
:
T
[
K
]
)
:
Builder
<
T
,
S
&
Record
<
K
,
T
[
K
]
>>
{
this
.
state
[
key
]
=
value
;
return
this
as
any
;
}
build
(
this
:
IsComplete
<
T
,
S
>
extends
true
?
this
:
never
)
:
T
{
return
this
.
state
as
T
;
}
}
interface
User
{
id
:
string
;
name
:
string
;
email
:
string
;
age
?
:
number
;
}
const
builder
=
new
Builder
<
User
>
(
)
;
const
user
=
builder
.
set
(
"id"
,
"1"
)
.
set
(
"name"
,
"John"
)
.
set
(
"email"
,
"john@example.com"
)
.
build
(
)
;
// OK: all required fields set
// const incomplete = builder
// .set("id", "1")
// .build(); // Error: missing required fields
Pattern 4: Deep Readonly/Partial
type
DeepReadonly
<
T
>
=
{
readonly
[
P
in
keyof
T
]
:
T
[
P
]
extends
object
?
T
[
P
]
extends
Function
?
T
[
P
]
:
DeepReadonly
<
T
[
P
]
>
:
T
[
P
]
;
}
;
type
DeepPartial
<
T
>
=
{
[
P
in
keyof
T
]
?
:
T
[
P
]
extends
object
?
T
[
P
]
extends
Array
<
infer
U
>
?
Array
<
DeepPartial
<
U
>>
:
DeepPartial
<
T
[
P
]
>
:
T
[
P
]
;
}
;
interface
Config
{
server
:
{
host
:
string
;
port
:
number
;
ssl
:
{
enabled
:
boolean
;
cert
:
string
;
}
;
}
;
database
:
{
url
:
string
;
pool
:
{
min
:
number
;
max
:
number
;
}
;
}
;
}
type
ReadonlyConfig
=
DeepReadonly
<
Config
>
;
// All nested properties are readonly
type
PartialConfig
=
DeepPartial
<
Config
>
;
// All nested properties are optional
Pattern 5: Type-Safe Form Validation
type
ValidationRule
<
T
>
=
{
validate
:
(
value
:
T
)
=>
boolean
;
message
:
string
;
}
;
type
FieldValidation
<
T
>
=
{
[
K
in
keyof
T
]
?
:
ValidationRule
<
T
[
K
]
>
[
]
;
}
;
type
ValidationErrors
<
T
>
=
{
[
K
in
keyof
T
]
?
:
string
[
]
;
}
;
class
FormValidator
<
T
extends
Record
<
string
,
any
>>
{
constructor
(
private
rules
:
FieldValidation
<
T
>
)
{
}
validate
(
data
:
T
)
:
ValidationErrors
<
T
>
|
null
{
const
errors
:
ValidationErrors
<
T
>
=
{
}
;
let
hasErrors
=
false
;
for
(
const
key
in
this
.
rules
)
{
const
fieldRules
=
this
.
rules
[
key
]
;
const
value
=
data
[
key
]
;
if
(
fieldRules
)
{
const
fieldErrors
:
string
[
]
=
[
]
;
for
(
const
rule
of
fieldRules
)
{
if
(
!
rule
.
validate
(
value
)
)
{
fieldErrors
.
push
(
rule
.
message
)
;
}
}
if
(
fieldErrors
.
length
>
0
)
{
errors
[
key
]
=
fieldErrors
;
hasErrors
=
true
;
}
}
}
return
hasErrors
?
errors
:
null
;
}
}
interface
LoginForm
{
email
:
string
;
password
:
string
;
}
const
validator
=
new
FormValidator
<
LoginForm
>
(
{
email
:
[
{
validate
:
(
v
)
=>
v
.
includes
(
"@"
)
,
message
:
"Email must contain @"
,
}
,
{
validate
:
(
v
)
=>
v
.
length
>
0
,
message
:
"Email is required"
,
}
,
]
,
password
:
[
{
validate
:
(
v
)
=>
v
.
length
>=
8
,
message
:
"Password must be at least 8 characters"
,
}
,
]
,
}
)
;
const
errors
=
validator
.
validate
(
{
email
:
"invalid"
,
password
:
"short"
,
}
)
;
// Type: { email?: string[]; password?: string[]; } | null
Pattern 6: Discriminated Unions
type
Success
<
T
>
=
{
status
:
"success"
;
data
:
T
;
}
;
type
Error
=
{
status
:
"error"
;
error
:
string
;
}
;
type
Loading
=
{
status
:
"loading"
;
}
;
type
AsyncState
<
T
>
=
Success
<
T
>
|
Error
|
Loading
;
function
handleState
<
T
>
(
state
:
AsyncState
<
T
>
)
:
void
{
switch
(
state
.
status
)
{
case
"success"
:
console
.
log
(
state
.
data
)
;
// Type: T
break
;
case
"error"
:
console
.
log
(
state
.
error
)
;
// Type: string
break
;
case
"loading"
:
console
.
log
(
"Loading..."
)
;
break
;
}
}
// Type-safe state machine
type
State
=
|
{
type
:
"idle"
}
|
{
type
:
"fetching"
;
requestId
:
string
}
|
{
type
:
"success"
;
data
:
any
}
|
{
type
:
"error"
;
error
:
Error
}
;
type
Event
=
|
{
type
:
"FETCH"
;
requestId
:
string
}
|
{
type
:
"SUCCESS"
;
data
:
any
}
|
{
type
:
"ERROR"
;
error
:
Error
}
|
{
type
:
"RESET"
}
;
function
reducer
(
state
:
State
,
event
:
Event
)
:
State
{
switch
(
state
.
type
)
{
case
"idle"
:
return
event
.
type
===
"FETCH"
?
{
type
:
"fetching"
,
requestId
:
event
.
requestId
}
:
state
;
case
"fetching"
:
if
(
event
.
type
===
"SUCCESS"
)
{
return
{
type
:
"success"
,
data
:
event
.
data
}
;
}
if
(
event
.
type
===
"ERROR"
)
{
return
{
type
:
"error"
,
error
:
event
.
error
}
;
}
return
state
;
case
"success"
:
case
"error"
:
return
event
.
type
===
"RESET"
?
{
type
:
"idle"
}
:
state
;
}
}
Type Inference Techniques
1. Infer Keyword
// Extract array element type
type
ElementType
<
T
>
=
T
extends
(
infer
U
)
[
]
?
U
:
never
;
type
NumArray
=
number
[
]
;
type
Num
=
ElementType
<
NumArray
>
;
// number
// Extract promise type
type
PromiseType
<
T
>
=
T
extends
Promise
<
infer
U
>
?
U
:
never
;
type
AsyncNum
=
PromiseType
<
Promise
<
number
>>
;
// number
// Extract function parameters
type
Parameters
<
T
>
=
T
extends
(
...
args
:
infer
P
)
=>
any
?
P
:
never
;
function
foo
(
a
:
string
,
b
:
number
)
{
}
type
FooParams
=
Parameters
<
typeof
foo
>
;
// [string, number]
2. Type Guards
function
isString
(
value
:
unknown
)
:
value
is
string
{
return
typeof
value
===
"string"
;
}
function
isArrayOf
<
T
>
(
value
:
unknown
,
guard
:
(
item
:
unknown
)
=>
item
is
T
,
)
:
value
is
T
[
]
{
return
Array
.
isArray
(
value
)
&&
value
.
every
(
guard
)
;
}
const
data
:
unknown
=
[
"a"
,
"b"
,
"c"
]
;
if
(
isArrayOf
(
data
,
isString
)
)
{
data
.
forEach
(
(
s
)
=>
s
.
toUpperCase
(
)
)
;
// Type: string[]
}
3. Assertion Functions
function
assertIsString
(
value
:
unknown
)
:
asserts
value
is
string
{
if
(
typeof
value
!==
"string"
)
{
throw
new
Error
(
"Not a string"
)
;
}
}
function
processValue
(
value
:
unknown
)
{
assertIsString
(
value
)
;
// value is now typed as string
console
.
log
(
value
.
toUpperCase
(
)
)
;
}
Best Practices
Use
unknown
over
any
Enforce type checking
Prefer
interface
for object shapes
Better error messages
Use
type
for unions and complex types
More flexible
Leverage type inference
Let TypeScript infer when possible
Create helper types
Build reusable type utilities
Use const assertions
Preserve literal types
Avoid type assertions
Use type guards instead
Document complex types
Add JSDoc comments
Use strict mode
Enable all strict compiler options
Test your types
Use type tests to verify type behavior
Type Testing
// Type assertion tests
type
AssertEqual
<
T
,
U
>
=
[
T
]
extends
[
U
]
?
[
U
]
extends
[
T
]
?
true
:
false
:
false
;
type
Test1
=
AssertEqual
<
string
,
string
>
;
// true
type
Test2
=
AssertEqual
<
string
,
number
>
;
// false
type
Test3
=
AssertEqual
<
string
|
number
,
string
>
;
// false
// Expect error helper
type
ExpectError
<
T
extends
never
>
=
T
;
// Example usage
type
ShouldError
=
ExpectError
<
AssertEqual
<
string
,
number
>>
;
Common Pitfalls
Over-using
any
Defeats the purpose of TypeScript
Ignoring strict null checks
Can lead to runtime errors
Too complex types
Can slow down compilation
Not using discriminated unions
Misses type narrowing opportunities
Forgetting readonly modifiers
Allows unintended mutations
Circular type references
Can cause compiler errors
Not handling edge cases
Like empty arrays or null values Performance Considerations Avoid deeply nested conditional types Use simple types when possible Cache complex type computations Limit recursion depth in recursive types Use build tools to skip type checking in production
返回排行榜