type PlayerOne = { kind: "playerOne"; name: string; }; type PlayerTwo = { kind: "playerTwo"; name: string; }; type Player = PlayerOne | PlayerTwo; type Point = "love" | "15" | "30"; type Deuce = { kind: "deuce"; }; type PointsData = { kind: "points"; playerOnePoint: Point; playerTwoPoint: Point; }; type FortyData = { kind: "forty"; player: Player; otherPlayerPoint: Point; }; type Advantage = { kind: "advantage", player: Player }; type Game = { kind: "game", player: Player // otherPlayerPoint: ? Not representable here. }; type Score = Deuce | PointsData | FortyData | Advantage | Game; function scoreWhenDeuce(player: Player): Advantage { return { kind: "advantage", player, } } function scoreWhenAdvantage(score: Advantage, player: Player): Game | Deuce { return score.player.kind === player.kind ? { kind: "game", player, } : { kind: "deuce", }; } function incrementPoints(point: Point): "15" | "30" | undefined { switch (point) { case "love": return "15"; case "15": return "30"; case "30": return undefined; } } function scoreWhenForty(score: FortyData, player: Player): Game | Deuce | FortyData { if (score.player.kind === player.kind) { return { kind: "game", player, } } else { // "15" | "30" | undefined is a subset of Point | undefined const newPoints: Point | undefined = incrementPoints(score.otherPlayerPoint); return newPoints === undefined ? { kind: "deuce" } : { ...score, otherPlayerPoint: newPoints }; } }