Intersection types

Explains the intersection type `&`, which describes a value that is all of several types at once. In CruftScript the form is parsed but not yet modeled: it is matched by exact spelling, never merged, conflicts are not detected, and it is refused wherever a precise type is required. Write a single object type instead.

An intersection type describes a value that is all of several types at once. You write it with & between the parts, so { a: number } & { b: string } means a value that has both an a and a b.

type AB = { a: number } & { b: string }

In TypeScript this is a full member of the type system: the two sides are merged into a single structural shape, conflicting members collapse, and A & B means the same thing as B & A. CruftScript does not do any of that yet. The & form is recognized by the parser, but the checker does not model it. It is a placeholder today, and it behaves in ways you should know before you reach for it.

What the checker actually does with &

The current checker does not build an intersection. It carries the & form along verbatim, as an opaque spelling — a piece of text it knows is a type but cannot reason about. That has four consequences you can observe.

It is matched only by exact string identity. Two intersection types are treated as the same type only when their spellings match character for character. Nothing looks inside them.

type AB = { a: number } & { b: string }
type BA = { b: string } & { a: number }
// AB and BA are NOT the same type here: the spellings differ

Members are never merged. Because the two sides are never combined, a value of an intersection type is not seen as having the union of both sides' members. You cannot read a and b off an AB value the way you could in TypeScript, because the checker never formed the shape that has both.

Conflicting members do not collapse to never. In TypeScript, { x: number } & { x: string } reduces to a member of type never, because no value can be both. CruftScript does not perform that reduction. The conflict is carried, not detected.

It is refused wherever a precise type is required. Any position where the checker needs to know the exact type rejects an & form outright. That includes using it as a generic argument and having a value enter your program from outside with an intersection as its declared type. An & form with an any inside it is rejected as well.

function id<T>(x: T): T { return x }
id<{ a: number } & { b: string }>(v)   // error: intersection not accepted here

Why it works this way

The honest reason is that the feature is not built. Rather than pretend an & form is something the checker understands, CruftScript treats it as opaque and refuses it at every point where trusting it could go wrong. That keeps an unmodeled type from leaking into a place that needs precision and being silently accepted. It is far less capable than TypeScript's intersection, and it is deliberately conservative: when in doubt, it refuses rather than guesses.

If your goal was to combine two object shapes, write a single object type that lists all the members you want. That is a real type the checker models fully.

Limitations

  • No structural merge. The two sides are not combined. You cannot use an intersection value as if it had all the members of both parts.
  • Order matters. A & B is not equivalent to B & A, because matching is by exact spelling. This does not match TypeScript.
  • Conflicts are not detected. Contradictory members are carried as written, not reduced to never.
  • Refused where precision is required. A generic argument, a value entering from outside your program, or any position needing an exact type will reject an & form. An any member is rejected outright.