A type alias can reference itself when the reference sits somewhere resolved lazily: an object property, an array or tuple element (TS 3.7), a mapped type's property type, or a branch of a conditional type (TS 4.1). A bare type A = A | string is still an error (TS2456).
DeepPartial is a mapped type that recurses on each property:
type DeepPartial<T> = T extends (...args: any) => any ? T
: T extends object ? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
The first branch keeps functions callable, the second recurses on objects (arrays and tuples keep their shape, since the mapped type is homomorphic), and primitives fall through. The naive { [K in keyof T]?: DeepPartial<T[K]> } alone turns function properties into Naive<(v: string) => void>, which is not callable.
Limits.
- Depth cap. The checker bounds nested type instantiations. A non-tail-recursive conditional that returns something built from the recursive call (
C | GetChars<R>) fails with TS2589 ("excessively deep and possibly infinite") after only dozens of levels. - Tail recursion (TS 4.5). If a branch is just another conditional (the recursive call is in tail position), the compiler loops instead of nesting and allows about a thousand iterations. Rewrite with an accumulator parameter to get there.
- Cost. Recursive types slow checking and degrade error messages; the 4.1 notes tell you to use them sparingly.
Pitfalls in the type itself: built-in classes like Date, Map, Set and Promise are objects, so DeepPartial maps their methods into optional ones. Add a branch for the types you want treated as leaves. Arrays get (T | undefined)[] elements because ? is applied per element.