PC パソコン

How to Fix “Expression Type-Checking Took Too Long” in Swift

When a Swift expression becomes too complex, Xcode may fail to determine its type within a reasonable amount of compile time.

A common diagnostic is:

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

You may also encounter this problem described as “expression type-checking took too long.”

This does not necessarily mean that the syntax is invalid. It usually means Swift’s type checker has too many possible generic types, overloads, closures, operators, optionals, or result-builder combinations to evaluate efficiently in one expression.

The most effective fix is to split the expression into smaller typed steps and provide explicit type information where inference is expensive.

  1. What does “Expression type-checking took too long” mean?
  2. Common error message
  3. What actually causes the problem?
  4. Typical situations where the error appears
  5. Common causes
    1. 1. One expression contains too many operations
    2. 2. A SwiftUI body is very large
    3. 3. A modifier chain is very long
    4. 4. Ternary expressions are deeply nested
    5. 5. Numeric types are mixed
    6. 6. Operators are heavily overloaded
    7. 7. Closure parameter types are underconstrained
    8. 8. A closure has a complicated return type
    9. 9. Several higher-order functions are chained together
    10. 10. A large array literal has complex elements
    11. 11. A large dictionary literal is underconstrained
    12. 12. String interpolation contains complex work
    13. 13. ForEach content is extremely complex
    14. 14. A view builder contains many conditional branches
    15. 15. Generic helper functions are deeply composed
    16. 16. An API has many overload candidates
    17. 17. Nested optional and generic types become difficult to infer
    18. 18. Custom operators are heavily used
    19. 19. A simple type error causes the solver to explore many alternatives
    20. 20. A small SwiftUI mistake expands into a large inference failure
  6. What to check first
    1. 1. Do not assume the highlighted line is the true cause
    2. 2. Split the expression in half
    3. 3. Add explicit types to intermediate values
  7. Solution 1: Break the expression into intermediate variables
  8. Solution 2: Add types to intermediate variables
  9. Solution 3: Specify closure parameter types
  10. Solution 4: Specify closure return types
  11. Solution 5: Specify the map result type
  12. Solution 6: Specify the compactMap result type
  13. Solution 7: Normalize numeric types
  14. Solution 8: Split large arithmetic expressions
  15. Solution 9: Replace nested ternaries with if statements
  16. Solution 10: Move complex work out of string interpolation
  17. Solution 11: Split a large SwiftUI body into computed subviews
  18. Solution 12: Extract large SwiftUI sections into separate View types
  19. Solution 13: Move ForEach row content into a separate View
  20. Solution 14: Extract conditional SwiftUI branches
  21. Solution 15: Precompute complex modifier arguments
  22. Solution 16: Give array literals an element type
  23. Solution 17: Give dictionary literals explicit key and value types
  24. Solution 18: Give nil a concrete optional type
  25. Solution 19: Replace overloaded function references with closures
  26. Solution 20: Break long optional chains into steps
  27. Solution 21: Add a result type to generic functions
  28. Solution 22: Design generic APIs to accept an explicit type
  29. Solution 23: Reduce excessive overloads in your own APIs
  30. Solution 24: Avoid overlapping overloads created by default arguments
  31. Solution 25: Match SwiftUI Picker selection and tag types
  32. Solution 26: Pass SwiftUI bindings correctly
  33. Solution 27: Unwrap optional model values before constructing complex views
  34. Solution 28: Reduce custom operator usage
  35. Solution 29: Fix more specific compiler errors first
  36. Solution 30: Reintroduce code incrementally
  37. If the error occurs in SwiftUI
  38. If the error occurs in map, filter, or compactMap
  39. If the error occurs in arithmetic
  40. If the error occurs in string interpolation
  41. If the expression does not look very long
  42. How to investigate slow type checking
  43. Will Clean Build Folder fix it?
  44. Should you delete DerivedData?
  45. What you should avoid
  46. Fast troubleshooting checklist
  47. Summary

What does “Expression type-checking took too long” mean?

Swift performs extensive type inference.

For simple code:

let number = 10 let text = "Hello"

the compiler can quickly infer Int and String.

Now consider a longer transformation:

let result = users .filter { $0.isActive } .compactMap { $0.profile } .map { $0.name } .filter { !$0.isEmpty } .sorted() .joined(separator: ", ")

This may still compile easily, but when long chains are combined with generic functions, overloaded initializers, optionals, custom operators, and SwiftUI result builders, the compiler may have to evaluate a much larger set of type constraints.

If the constraint solver cannot finish within a reasonable amount of time, compilation stops with a type-checking diagnostic.

Common error message

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

Related compiler diagnostics may describe an expression as too complex or may surface as generic inference and overload-resolution errors.

What actually causes the problem?

The compiler must resolve combinations involving features such as:

  • Function overloads
  • Generic types
  • Protocol constraints
  • Optionals
  • Numeric literals
  • Closures
  • Operator overloads
  • Result builders

Putting too many unresolved constraints into one expression can greatly increase the number of possibilities the compiler must consider.

Important: This is primarily a compile-time type-checking problem. It does not mean the expression will necessarily be slow when your app runs.

Typical situations where the error appears

  • A very large SwiftUI body
  • Deeply nested VStack and HStack structures
  • Nested ternary operators
  • Long map, filter, compactMap, and reduce chains
  • Long optional chains
  • Multiple generic functions composed together
  • Large arithmetic expressions
  • Mixed Int, Double, CGFloat, and other numeric types
  • Heavy use of overloaded operators
  • Complex expressions inside string interpolation
  • Large array or dictionary literals
  • Closures whose parameter types are unclear
  • APIs with many overloads
  • Very long SwiftUI modifier chains
  • Complex ForEach or GeometryReader content

Common causes

1. One expression contains too many operations

let result = users .filter { $0.isActive } .compactMap { $0.profile } .map { $0.name.trimmingCharacters(in: .whitespaces) } .filter { !$0.isEmpty } .sorted { $0.localizedCompare($1) == .orderedAscending } .joined(separator: ", ")

Each operation adds additional type constraints to the same expression.

2. A SwiftUI body is very large

var body: some View { VStack { ... HStack { ... } ... } }

SwiftUI views are composed from generic types, so very large view expressions can become expensive to type-check.

3. A modifier chain is very long

Text("Hello") .font(.headline) .padding() .background(...) .overlay(...) .clipShape(...) .shadow(...) .frame(...) .animation(...)

Each modifier creates another generic view type.

4. Ternary expressions are deeply nested

let text = isLoading ? "Loading" : hasError ? "Error" : isEmpty ? "Empty" : "Completed"

5. Numeric types are mixed

Expressions containing Int, Double, CGFloat, and overloaded arithmetic operators can create many possible operator candidates.

6. Operators are heavily overloaded

Even common operators such as + have many overloads across standard-library and custom types.

7. Closure parameter types are underconstrained

let result = values.map { value in ... }

If values itself has an unclear element type, the closure adds another level of unresolved inference.

8. A closure has a complicated return type

Different branches can produce different generic expressions, making the closure output harder to infer.

9. Several higher-order functions are chained together

map, compactMap, flatMap, filter, and reduce each introduce input and output constraints that may propagate through the entire chain.

10. A large array literal has complex elements

If each element contains generics, optionals, or overloaded initializers, inference can become expensive.

11. A large dictionary literal is underconstrained

The compiler must infer both key and value types.

12. String interpolation contains complex work

let message = "\(users.filter { $0.isActive }.map { $0.name }.joined(separator: ", "))"

13. ForEach content is extremely complex

A large view expression generated for every element can significantly increase SwiftUI type-checking complexity.

14. A view builder contains many conditional branches

Each if or switch branch contributes to the result-builder type.

15. Generic helper functions are deeply composed

Constraints from one generic call can propagate into several others.

16. An API has many overload candidates

The compiler may need to evaluate several possible function or initializer signatures.

17. Nested optional and generic types become difficult to infer

Result<User?, Error>?

Repeated transformations on types like this can become hard both for developers and the compiler.

18. Custom operators are heavily used

Custom precedence groups and operator overloads can increase the number of possible interpretations.

19. A simple type error causes the solver to explore many alternatives

One incorrect argument type can make Swift try multiple overloads before finally giving up with a type-checking timeout.

20. A small SwiftUI mistake expands into a large inference failure

A wrong property, binding, optional, or argument label inside a large view may surface as an error on the entire expression.

What to check first

1. Do not assume the highlighted line is the true cause

Xcode may underline the end of a long expression even when the problematic sub-expression appears much earlier.

2. Split the expression in half

This is one of the fastest ways to locate the problem.

let activeUsers = users.filter { $0.isActive }

let profiles =
activeUsers.compactMap { $0.profile }

let names =
profiles.map { $0.name }

let result =
names.joined(separator: ", ")

3. Add explicit types to intermediate values

let activeUsers: [User] = users.filter { $0.isActive }

This gives the compiler a concrete boundary and reduces the amount of inference required.

Tip: You usually do not need to rewrite an entire feature. Splitting one large expression into a few typed statements is often enough.

Solution 1: Break the expression into intermediate variables

Before:

let result = users .filter { $0.isActive } .compactMap { $0.profile } .map { $0.name } .sorted() .joined(separator: ", ")

After:

let activeUsers = users.filter { user in user.isActive }

let profiles =
activeUsers.compactMap { user in
user.profile
}

let names =
profiles.map { profile in
profile.name
}

let sortedNames =
names.sorted()

let result =
sortedNames.joined(separator: ", ")

Solution 2: Add types to intermediate variables

let activeUsers: [User] = users.filter { user in user.isActive }

let names: [String] =
activeUsers.map { user in
user.name
}

Solution 3: Specify closure parameter types

let names = users.map { (user: User) in
user.name

}

Solution 4: Specify closure return types

let names = users.map { (user: User) -> String in
user.name

}

Solution 5: Specify the map result type

let names: [String] = users.map { user in user.name }

Solution 6: Specify the compactMap result type

let ages: [Int] = ageStrings.compactMap { text in Int(text) }

Solution 7: Normalize numeric types

let count: Int = 10 let rate: Double = 1.5

let result =
Double(count) * rate

Keeping arithmetic operands on one concrete numeric type reduces operator-overload ambiguity.

Solution 8: Split large arithmetic expressions

Before:

let result = a * b / c + d * e / f - g * h

After:

let first = a * b / c

let second =
d * e / f

let third =
g * h

let result =
first + second - third

Solution 9: Replace nested ternaries with if statements

let status: String

if isLoading {
status = "Loading"
} else if hasError {
status = "Error"
} else if isEmpty {
status = "Empty"
} else {
status = "Ready"
}

This is often easier for both the compiler and future readers.

Solution 10: Move complex work out of string interpolation

Before:

let message = "Users: \(users.filter { $0.isActive }.map { $0.name }.joined(separator: ", "))"

After:

let activeUsers = users.filter { $0.isActive }

let names =
activeUsers.map { $0.name }

let joinedNames =
names.joined(separator: ", ")

let message =
"Users: (joinedNames)"

Solution 11: Split a large SwiftUI body into computed subviews

var body: some View { VStack { headerView profileView controlsView listView footerView } }

For example:

private var headerView: some View { HStack { Text("Profile") Spacer() } }

Solution 12: Extract large SwiftUI sections into separate View types

struct ProfileHeaderView: View { let name: String
var body: some View {
    HStack {
        Text(name)
        Spacer()
    }
}

}

Solution 13: Move ForEach row content into a separate View

ForEach(users) { user in UserRowView( user: user ) }

This dramatically reduces the complexity of the parent view builder when row content is large.

Solution 14: Extract conditional SwiftUI branches

var body: some View { Group { if isLoading { loadingView } else if let error { errorView(error) } else { contentView } } }

Solution 15: Precompute complex modifier arguments

Instead of doing several transformations inside a modifier call, calculate the value first and pass the result.

Solution 16: Give array literals an element type

let items: [MenuItem] = [ MenuItem(title: "Home"), MenuItem(title: "Settings") ]

Solution 17: Give dictionary literals explicit key and value types

let values: [String: Int] = [ "one": 1, "two": 2 ]

Solution 18: Give nil a concrete optional type

let value: String? = nil

Solution 19: Replace overloaded function references with closures

Instead of:

let strings = numbers.map(String.init)

use:

let strings: [String] = numbers.map { number in String(number) }

Solution 20: Break long optional chains into steps

let rawName = response?.user?.profile?.name

let trimmedName =
rawName?.trimmingCharacters(
in: .whitespacesAndNewlines
)

let name =
trimmedName?.lowercased()

Solution 21: Add a result type to generic functions

let user: User = try decode( data )

The result type gives the generic function a concrete specialization.

Solution 22: Design generic APIs to accept an explicit type

func decode<T: Decodable>( _ type: T.Type, from data: Data ) throws -> T { try JSONDecoder().decode( type, from: data ) }

let user =
try decode(
User.self,
from: data
)

Solution 23: Reduce excessive overloads in your own APIs

If many overloads differ only slightly, consider using clearer function names or fewer entry points.

Solution 24: Avoid overlapping overloads created by default arguments

Several overloads that can all be called with the same short argument list force the compiler to explore more candidates.

Solution 25: Match SwiftUI Picker selection and tag types

@State private var selection = 0

Picker(
"Option",
selection: $selection
) {
Text("One").tag(0)
Text("Two").tag(1)
}

Solution 26: Pass SwiftUI bindings correctly

@State private var name = ""

TextField(
"Name",
text: $name
)

Passing the wrapped value instead of the Binding can make initializer resolution more complicated or cause a separate type error.

Solution 27: Unwrap optional model values before constructing complex views

if let user { UserView( user: user ) }

Solution 28: Reduce custom operator usage

If several custom operators appear in one expression, replacing them with named helper functions can make the expression easier for the compiler to solve.

Solution 29: Fix more specific compiler errors first

If you also see diagnostics such as:

  • Cannot convert value of type
  • Generic parameter could not be inferred
  • Ambiguous use of
  • Missing argument

fix those first. The type-checking timeout may be only a secondary symptom.

Solution 30: Reintroduce code incrementally

If the cause is unclear, temporarily simplify or comment out part of the expression until it compiles.

Then add operations back one at a time. The point at which compilation becomes slow or fails usually identifies the problematic sub-expression.

If the error occurs in SwiftUI

Use this troubleshooting order:

  1. Split the body into subviews.
  2. Extract ForEach rows.
  3. Extract if and switch branches.
  4. Check Binding versus plain values.
  5. Unwrap optionals early.
  6. Check Picker selection and tag types.
  7. Precompute complex modifier arguments.
  8. Verify custom view argument types.
  9. Fix the earliest specific compiler error.

If the error occurs in map, filter, or compactMap

Split the pipeline and give each stage a concrete collection type:

let activeUsers: [User] = users.filter { user in user.isActive }

let profiles: [Profile] =
activeUsers.compactMap { user in
user.profile
}

let names: [String] =
profiles.map { profile in
profile.name
}

If the error occurs in arithmetic

Normalize the numeric types and break the calculation into steps:

let width: Double = Double(itemCount)

let scaledWidth: Double =
width * scale

let result: Double =
scaledWidth + padding

If the error occurs in string interpolation

let visibleCount = items.filter { item in item.isVisible }.count

let message =
"Visible items: (visibleCount)"

If the expression does not look very long

A short expression can still be expensive if it combines:

  • Many overloads
  • Complex generic constraints
  • Ambiguous numeric literals
  • nil or empty collections without context
  • Several protocol extensions
  • SwiftUI result-builder inference

Expression length is only one factor. The number of possible types is often more important.

How to investigate slow type checking

For a large project, Swift compiler frontend diagnostics can help identify expressions that take unusually long to type-check.

For example, during investigation you can add an option through Xcode’s:

Other Swift Flags

such as:

-Xfrontend -warn-long-expression-type-checking=100

The number represents a millisecond threshold.

For long function bodies, a related diagnostic option sometimes used during compile-time investigation is:

-Xfrontend -warn-long-function-bodies=100

Note: These are Swift frontend diagnostic options intended for investigation, not something every production target needs permanently. Availability and behavior can vary with the Swift and Xcode version.

Will Clean Build Folder fix it?

Usually not.

This error is caused primarily by source-level type-checking complexity.

Cleaning:

Product → Clean Build Folder

will not make the same underconstrained expression inherently easier to solve.

Should you delete DerivedData?

If Xcode indexing or cached build artifacts appear corrupted, you can use:

rm -rf ~/Library/Developer/Xcode/DerivedData/*

as a secondary troubleshooting step.

For a genuine type-checking timeout, splitting the expression and adding explicit types is the more important fix.

Note: Do not repeatedly delete DerivedData as the first response to this compiler error. Reduce the complexity of the expression first.

What you should avoid

Do not replace large parts of your code with Any or add forced casts everywhere just to reduce compiler errors.

Instead, prefer:

  • Intermediate variables
  • Concrete type annotations
  • Helper functions
  • Smaller SwiftUI views
  • Explicit closure parameter and return types

These preserve type safety while helping both the compiler and future readers understand the code.

Fast troubleshooting checklist

  1. Split the failing expression into two or more statements.
  2. Add types to intermediate variables.
  3. Specify closure parameter types.
  4. Specify closure return types.
  5. Break long map, filter, and compactMap chains apart.
  6. Normalize numeric types.
  7. Replace nested ternaries with if statements.
  8. Move work out of string interpolation.
  9. Split large SwiftUI bodies into subviews.
  10. Extract complex ForEach rows.
  11. Check Binding versus plain values.
  12. Unwrap optionals earlier.
  13. Type array and dictionary literals explicitly.
  14. Replace overloaded function references with closures.
  15. Fix the earliest concrete type error first.

Summary

Swift’s “Expression type-checking took too long” problem, commonly reported as “The compiler is unable to type-check this expression in reasonable time,” means the compiler cannot resolve all type constraints in one expression quickly enough.

Common causes include long transformation chains, large SwiftUI views, complex generic code, closure inference, overloaded operators, mixed numeric types, optionals, large literals, and overloaded APIs.

The most effective solution is to split large expressions into intermediate variables, helper functions, and smaller SwiftUI views.

Adding concrete types to variables, closure parameters, return values, collection elements, and generic results further reduces the number of candidates the compiler must evaluate.

For SwiftUI, break up large bodies and ForEach content, and verify bindings, optionals, Picker selection types, and custom view arguments.

Cleaning the build folder or deleting DerivedData may help with unrelated stale build artifacts, but the real fix for a genuine type-checking timeout is to make the source expression simpler and more explicitly typed.