Skip to content

Commit

Permalink
Clarify that the rule applies to both local variables and instance pr…
Browse files Browse the repository at this point in the history
…operties, simplify examples
  • Loading branch information
calda committed Apr 11, 2024
1 parent 5a39cdc commit dd1db39
Showing 1 changed file with 54 additions and 38 deletions.
92 changes: 54 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,13 @@ _You can enable the following settings in Xcode by running [this script](resourc
let sun = Star(mass: 1.989e30)
let earth = Planet.earth

// NOT RECOMMENDED. However, since the linter doesn't have full type information, this is not enforced automatically.
let moon: Moon = earth.moon // returns `Moon`

// RIGHT
let moon = earth.moon
let moon: PlanetaryBody? = earth.moon

// WRONG: Most literals provide a default type that can be inferred.
let enableGravity: Bool = true
let numberOfPlanets: Int = 8
Expand Down Expand Up @@ -369,70 +376,79 @@ _You can enable the following settings in Xcode by running [this script](resourc

</details>

* <a id='infer-property-types'></a>(<a href='#infer-property-types'>link</a>) **Prefer letting the type of a property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** [![SwiftFormat: preferInferredTypes](https://img.shields.io/badge/SwiftFormat-preferInferredTypes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#preferInferredTypes)
* <a id='infer-property-types'></a>(<a href='#infer-property-types'>link</a>) **Prefer letting the type of a variable or property be inferred from the right-hand-side value rather than writing the type explicitly on the left-hand side.** [![SwiftFormat: preferInferredTypes](https://img.shields.io/badge/SwiftFormat-preferInferredTypes-7B0051.svg)](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md#preferInferredTypes)

<details>

Prefer using inferred types when the right-hand-side value is a static member with a leading dot (e.g. an `init`, a `static` property / function, or an enum case). This applies to both local variables and property declarations:

```swift
// WRONG
let sun: Star = .init(mass: 1.989e30)
let earth: Planet = .earth
struct SolarSystemBuilder {
let sun: Star = .init(mass: 1.989e30)
let earth: Planet = .earth

func setUp() {
let galaxy: Galaxy = .andromeda
let system: SolarSystem = .init(sun, earth)
galaxy.add(system)
}
}

// RIGHT
let sun = Star(mass: 1.989e30)
let earth = Planet.earth
struct SolarSystemBuilder {
let sun = Star(mass: 1.989e30)
let earth = Planet.earth

// ALSO RIGHT: Explicit types are required when there is no right-hand-side value.
let sun: Star
let earth: Planet

// ALSO RIGHT: Explicit types can be necessary when the right-hand side has
// a different type from the one written explicitly on the left-hand side.
let naturalSatellite: PlanetaryBody? = Moon(mass: 7.347e22)
let moon: PlanetaryBody? = nil // nil literals are typeless
let numberOfPlanets: UInt = 8 // integer literals default to `Int`
let sunMass: CGFloat = 1.989e30 // floating-point literals default to `Double`
let planets: [Planet] = [] // empty collection literals are typeless

// ALSO RIGHT: Some of the examples above can also be written idiomatically without an explicit type on
// the left-hand-side, by instead giving the right-hand side value an explicit type. Either style is fine.
let numberOfPlanets = UInt(8)
let sunMass = CGFloat(1.989e30)
let planets = [Planet]()
func setUp() {
let galaxy = Galaxy.andromeda
let system = SolarSystem(sun, earth)
galaxy.add(system)
}
}
```
There are some rarer cases where the inferred type syntax has a different meaning than the explicit type syntax. In these cases, the explicit type syntax is still permitted:

Explicit types are still permitted in other cases:

```swift
extension String {
static let earth = "Earth"
}
// RIGHT: There is no right-hand-side value, so an explicit type is required.
let sun: Star

// RIGHT: If the property's type is optional, moving the optional type
// to the right-hand side may result in invalid code.
let planetName: String? = .earth

// RIGHT: The right-hand-side is not a static member of the left-hand type.
let moon: PlantaryBody = earth.moon
let sunMass: Float = 1.989e30
let planets: [Planet] = []
let venusMoon: Moon? = nil
```

There are some rare cases where the inferred type syntax has a different meaning than the explicit type syntax. In these cases, the explicit type syntax is still permitted:

```swift
// WRONG: fails with "error: type 'String?' has no member 'foo'"
// if `earth` is defined in an extension on `String` rather than `Optional<String>`.
let planetName = String?.earth

// RIGHT
let planetName: String? = .earth
```

```swift
struct SaturnOutline: ShapeStyle { ... }

extension ShapeStyle where Self == SaturnOutline {
static var saturnOutline: SaturnOutline {
SaturnOutline()
}
}


// WRONG: fails with "error: static member 'saturnOutline' cannot be used on protocol metatype '(any ShapeStyle).Type'"
let myShape2 = (any ShapeStyle).myShape

// RIGHT: If the property's type is an existential / protocol type, moving the type
// to the right-hand side will result in invalid code if the value is defined in an
// extension like `extension ShapeStyle where Self == SaturnOutline`.
// SwiftFormat autocorrect detects this case by checking for the existential `any` keyword.
let myShape1: any ShapeStyle = .saturnOutline

// WRONG: fails with "error: static member 'saturnOutline' cannot be used on protocol metatype '(any ShapeStyle).Type'"
let myShape2 = (any ShapeStyle).myShape
```

</details>
Expand Down

0 comments on commit dd1db39

Please sign in to comment.