Skip to content

Commit

Permalink
Discriminant of type never should never be matched (#50755)
Browse files Browse the repository at this point in the history
* Discriminant of type 'never' should never be matched

* Add tests
  • Loading branch information
ahejlsberg committed Sep 13, 2022
1 parent e37ea53 commit 60963d7
Show file tree
Hide file tree
Showing 8 changed files with 1,101 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Expand Up @@ -25130,7 +25130,7 @@ namespace ts {
const narrowedPropType = narrowType(propType);
return filterType(type, t => {
const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName);
return !(narrowedPropType.flags & TypeFlags.Never) && areTypesComparable(narrowedPropType, discriminantType);
return !(discriminantType.flags & TypeFlags.Never) && !(narrowedPropType.flags & TypeFlags.Never) && areTypesComparable(narrowedPropType, discriminantType);
});
}

Expand Down
143 changes: 143 additions & 0 deletions tests/baselines/reference/neverAsDiscriminantType(strict=false).js
@@ -0,0 +1,143 @@
//// [neverAsDiscriminantType.ts]
type Foo1 = { kind: 'a', a: number } | { kind: 'b' } | { kind: never };

function f1(foo: Foo1) {
if (foo.kind === 'a') {
foo.a;
}
}

type Foo2 = { kind?: 'a', a: number } | { kind?: 'b' } | { kind?: never };

function f2(foo: Foo2) {
if (foo.kind === 'a') {
foo.a;
}
}

// Repro from #50716

export interface GatewayPayloadStructure<O extends GatewayOpcode, T extends keyof GatewayEvents, D> {
op: O
d: D
t?: T
s?: number
}

export type GatewayPayload = {
[O in GatewayOpcode]: O extends GatewayOpcode.DISPATCH
? {
[T in keyof GatewayEvents]: GatewayPayloadStructure<GatewayOpcode.DISPATCH, T, GatewayEvents[T]>
}[keyof GatewayEvents]
: GatewayPayloadStructure<O, never, O extends keyof GatewayParams ? GatewayParams[O] : never>
}[GatewayOpcode]

export interface GatewayParams {
[GatewayOpcode.HELLO]: { b: 1 }
}

export enum GatewayOpcode {
DISPATCH = 0,
HEARTBEAT = 1,
IDENTIFY = 2,
PRESENCE_UPDATE = 3,
VOICE_STATE_UPDATE = 4,
RESUME = 6,
RECONNECT = 7,
REQUEST_GUILD_MEMBERS = 8,
INVALID_SESSION = 9,
HELLO = 10,
HEARTBEAT_ACK = 11,
}

export interface GatewayEvents {
MESSAGE_CREATE: { a: 1 }
MESSAGE_UPDATE: { a: 2 }
MESSAGE_DELETE: { a: 3 }
}

function assertMessage(event: { a: 1 }) { }

export async function adaptSession(input: GatewayPayload) {
if (input.t === 'MESSAGE_CREATE') {
assertMessage(input.d)
}
}


//// [neverAsDiscriminantType.js]
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
exports.adaptSession = exports.GatewayOpcode = void 0;
function f1(foo) {
if (foo.kind === 'a') {
foo.a;
}
}
function f2(foo) {
if (foo.kind === 'a') {
foo.a;
}
}
var GatewayOpcode;
(function (GatewayOpcode) {
GatewayOpcode[GatewayOpcode["DISPATCH"] = 0] = "DISPATCH";
GatewayOpcode[GatewayOpcode["HEARTBEAT"] = 1] = "HEARTBEAT";
GatewayOpcode[GatewayOpcode["IDENTIFY"] = 2] = "IDENTIFY";
GatewayOpcode[GatewayOpcode["PRESENCE_UPDATE"] = 3] = "PRESENCE_UPDATE";
GatewayOpcode[GatewayOpcode["VOICE_STATE_UPDATE"] = 4] = "VOICE_STATE_UPDATE";
GatewayOpcode[GatewayOpcode["RESUME"] = 6] = "RESUME";
GatewayOpcode[GatewayOpcode["RECONNECT"] = 7] = "RECONNECT";
GatewayOpcode[GatewayOpcode["REQUEST_GUILD_MEMBERS"] = 8] = "REQUEST_GUILD_MEMBERS";
GatewayOpcode[GatewayOpcode["INVALID_SESSION"] = 9] = "INVALID_SESSION";
GatewayOpcode[GatewayOpcode["HELLO"] = 10] = "HELLO";
GatewayOpcode[GatewayOpcode["HEARTBEAT_ACK"] = 11] = "HEARTBEAT_ACK";
})(GatewayOpcode = exports.GatewayOpcode || (exports.GatewayOpcode = {}));
function assertMessage(event) { }
function adaptSession(input) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (input.t === 'MESSAGE_CREATE') {
assertMessage(input.d);
}
return [2 /*return*/];
});
});
}
exports.adaptSession = adaptSession;
@@ -0,0 +1,197 @@
=== tests/cases/compiler/neverAsDiscriminantType.ts ===
type Foo1 = { kind: 'a', a: number } | { kind: 'b' } | { kind: never };
>Foo1 : Symbol(Foo1, Decl(neverAsDiscriminantType.ts, 0, 0))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 0, 13))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 0, 24))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 0, 40))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 0, 56))

function f1(foo: Foo1) {
>f1 : Symbol(f1, Decl(neverAsDiscriminantType.ts, 0, 71))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 2, 12))
>Foo1 : Symbol(Foo1, Decl(neverAsDiscriminantType.ts, 0, 0))

if (foo.kind === 'a') {
>foo.kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 0, 13), Decl(neverAsDiscriminantType.ts, 0, 40), Decl(neverAsDiscriminantType.ts, 0, 56))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 2, 12))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 0, 13), Decl(neverAsDiscriminantType.ts, 0, 40), Decl(neverAsDiscriminantType.ts, 0, 56))

foo.a;
>foo.a : Symbol(a, Decl(neverAsDiscriminantType.ts, 0, 24))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 2, 12))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 0, 24))
}
}

type Foo2 = { kind?: 'a', a: number } | { kind?: 'b' } | { kind?: never };
>Foo2 : Symbol(Foo2, Decl(neverAsDiscriminantType.ts, 6, 1))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 8, 13))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 8, 25))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 8, 41))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 8, 58))

function f2(foo: Foo2) {
>f2 : Symbol(f2, Decl(neverAsDiscriminantType.ts, 8, 74))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 10, 12))
>Foo2 : Symbol(Foo2, Decl(neverAsDiscriminantType.ts, 6, 1))

if (foo.kind === 'a') {
>foo.kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 8, 13), Decl(neverAsDiscriminantType.ts, 8, 41), Decl(neverAsDiscriminantType.ts, 8, 58))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 10, 12))
>kind : Symbol(kind, Decl(neverAsDiscriminantType.ts, 8, 13), Decl(neverAsDiscriminantType.ts, 8, 41), Decl(neverAsDiscriminantType.ts, 8, 58))

foo.a;
>foo.a : Symbol(a, Decl(neverAsDiscriminantType.ts, 8, 25))
>foo : Symbol(foo, Decl(neverAsDiscriminantType.ts, 10, 12))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 8, 25))
}
}

// Repro from #50716

export interface GatewayPayloadStructure<O extends GatewayOpcode, T extends keyof GatewayEvents, D> {
>GatewayPayloadStructure : Symbol(GatewayPayloadStructure, Decl(neverAsDiscriminantType.ts, 14, 1))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 18, 41))
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))
>T : Symbol(T, Decl(neverAsDiscriminantType.ts, 18, 65))
>GatewayEvents : Symbol(GatewayEvents, Decl(neverAsDiscriminantType.ts, 49, 1))
>D : Symbol(D, Decl(neverAsDiscriminantType.ts, 18, 96))

op: O
>op : Symbol(GatewayPayloadStructure.op, Decl(neverAsDiscriminantType.ts, 18, 101))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 18, 41))

d: D
>d : Symbol(GatewayPayloadStructure.d, Decl(neverAsDiscriminantType.ts, 19, 9))
>D : Symbol(D, Decl(neverAsDiscriminantType.ts, 18, 96))

t?: T
>t : Symbol(GatewayPayloadStructure.t, Decl(neverAsDiscriminantType.ts, 20, 8))
>T : Symbol(T, Decl(neverAsDiscriminantType.ts, 18, 65))

s?: number
>s : Symbol(GatewayPayloadStructure.s, Decl(neverAsDiscriminantType.ts, 21, 9))
}

export type GatewayPayload = {
>GatewayPayload : Symbol(GatewayPayload, Decl(neverAsDiscriminantType.ts, 23, 1))

[O in GatewayOpcode]: O extends GatewayOpcode.DISPATCH
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 26, 5))
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 26, 5))
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))
>DISPATCH : Symbol(GatewayOpcode.DISPATCH, Decl(neverAsDiscriminantType.ts, 37, 27))

? {
[T in keyof GatewayEvents]: GatewayPayloadStructure<GatewayOpcode.DISPATCH, T, GatewayEvents[T]>
>T : Symbol(T, Decl(neverAsDiscriminantType.ts, 28, 9))
>GatewayEvents : Symbol(GatewayEvents, Decl(neverAsDiscriminantType.ts, 49, 1))
>GatewayPayloadStructure : Symbol(GatewayPayloadStructure, Decl(neverAsDiscriminantType.ts, 14, 1))
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))
>DISPATCH : Symbol(GatewayOpcode.DISPATCH, Decl(neverAsDiscriminantType.ts, 37, 27))
>T : Symbol(T, Decl(neverAsDiscriminantType.ts, 28, 9))
>GatewayEvents : Symbol(GatewayEvents, Decl(neverAsDiscriminantType.ts, 49, 1))
>T : Symbol(T, Decl(neverAsDiscriminantType.ts, 28, 9))

}[keyof GatewayEvents]
>GatewayEvents : Symbol(GatewayEvents, Decl(neverAsDiscriminantType.ts, 49, 1))

: GatewayPayloadStructure<O, never, O extends keyof GatewayParams ? GatewayParams[O] : never>
>GatewayPayloadStructure : Symbol(GatewayPayloadStructure, Decl(neverAsDiscriminantType.ts, 14, 1))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 26, 5))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 26, 5))
>GatewayParams : Symbol(GatewayParams, Decl(neverAsDiscriminantType.ts, 31, 16))
>GatewayParams : Symbol(GatewayParams, Decl(neverAsDiscriminantType.ts, 31, 16))
>O : Symbol(O, Decl(neverAsDiscriminantType.ts, 26, 5))

}[GatewayOpcode]
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))

export interface GatewayParams {
>GatewayParams : Symbol(GatewayParams, Decl(neverAsDiscriminantType.ts, 31, 16))

[GatewayOpcode.HELLO]: { b: 1 }
>[GatewayOpcode.HELLO] : Symbol(GatewayParams[GatewayOpcode.HELLO], Decl(neverAsDiscriminantType.ts, 33, 32))
>GatewayOpcode.HELLO : Symbol(GatewayOpcode.HELLO, Decl(neverAsDiscriminantType.ts, 46, 24))
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))
>HELLO : Symbol(GatewayOpcode.HELLO, Decl(neverAsDiscriminantType.ts, 46, 24))
>b : Symbol(b, Decl(neverAsDiscriminantType.ts, 34, 28))
}

export enum GatewayOpcode {
>GatewayOpcode : Symbol(GatewayOpcode, Decl(neverAsDiscriminantType.ts, 35, 1))

DISPATCH = 0,
>DISPATCH : Symbol(GatewayOpcode.DISPATCH, Decl(neverAsDiscriminantType.ts, 37, 27))

HEARTBEAT = 1,
>HEARTBEAT : Symbol(GatewayOpcode.HEARTBEAT, Decl(neverAsDiscriminantType.ts, 38, 17))

IDENTIFY = 2,
>IDENTIFY : Symbol(GatewayOpcode.IDENTIFY, Decl(neverAsDiscriminantType.ts, 39, 18))

PRESENCE_UPDATE = 3,
>PRESENCE_UPDATE : Symbol(GatewayOpcode.PRESENCE_UPDATE, Decl(neverAsDiscriminantType.ts, 40, 17))

VOICE_STATE_UPDATE = 4,
>VOICE_STATE_UPDATE : Symbol(GatewayOpcode.VOICE_STATE_UPDATE, Decl(neverAsDiscriminantType.ts, 41, 24))

RESUME = 6,
>RESUME : Symbol(GatewayOpcode.RESUME, Decl(neverAsDiscriminantType.ts, 42, 27))

RECONNECT = 7,
>RECONNECT : Symbol(GatewayOpcode.RECONNECT, Decl(neverAsDiscriminantType.ts, 43, 15))

REQUEST_GUILD_MEMBERS = 8,
>REQUEST_GUILD_MEMBERS : Symbol(GatewayOpcode.REQUEST_GUILD_MEMBERS, Decl(neverAsDiscriminantType.ts, 44, 18))

INVALID_SESSION = 9,
>INVALID_SESSION : Symbol(GatewayOpcode.INVALID_SESSION, Decl(neverAsDiscriminantType.ts, 45, 30))

HELLO = 10,
>HELLO : Symbol(GatewayOpcode.HELLO, Decl(neverAsDiscriminantType.ts, 46, 24))

HEARTBEAT_ACK = 11,
>HEARTBEAT_ACK : Symbol(GatewayOpcode.HEARTBEAT_ACK, Decl(neverAsDiscriminantType.ts, 47, 15))
}

export interface GatewayEvents {
>GatewayEvents : Symbol(GatewayEvents, Decl(neverAsDiscriminantType.ts, 49, 1))

MESSAGE_CREATE: { a: 1 }
>MESSAGE_CREATE : Symbol(GatewayEvents.MESSAGE_CREATE, Decl(neverAsDiscriminantType.ts, 51, 32))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 52, 21))

MESSAGE_UPDATE: { a: 2 }
>MESSAGE_UPDATE : Symbol(GatewayEvents.MESSAGE_UPDATE, Decl(neverAsDiscriminantType.ts, 52, 28))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 53, 21))

MESSAGE_DELETE: { a: 3 }
>MESSAGE_DELETE : Symbol(GatewayEvents.MESSAGE_DELETE, Decl(neverAsDiscriminantType.ts, 53, 28))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 54, 21))
}

function assertMessage(event: { a: 1 }) { }
>assertMessage : Symbol(assertMessage, Decl(neverAsDiscriminantType.ts, 55, 1))
>event : Symbol(event, Decl(neverAsDiscriminantType.ts, 57, 23))
>a : Symbol(a, Decl(neverAsDiscriminantType.ts, 57, 31))

export async function adaptSession(input: GatewayPayload) {
>adaptSession : Symbol(adaptSession, Decl(neverAsDiscriminantType.ts, 57, 43))
>input : Symbol(input, Decl(neverAsDiscriminantType.ts, 59, 35))
>GatewayPayload : Symbol(GatewayPayload, Decl(neverAsDiscriminantType.ts, 23, 1))

if (input.t === 'MESSAGE_CREATE') {
>input.t : Symbol(GatewayPayloadStructure.t, Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8) ... and 8 more)
>input : Symbol(input, Decl(neverAsDiscriminantType.ts, 59, 35))
>t : Symbol(GatewayPayloadStructure.t, Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8), Decl(neverAsDiscriminantType.ts, 20, 8) ... and 8 more)

assertMessage(input.d)
>assertMessage : Symbol(assertMessage, Decl(neverAsDiscriminantType.ts, 55, 1))
>input.d : Symbol(GatewayPayloadStructure.d, Decl(neverAsDiscriminantType.ts, 19, 9))
>input : Symbol(input, Decl(neverAsDiscriminantType.ts, 59, 35))
>d : Symbol(GatewayPayloadStructure.d, Decl(neverAsDiscriminantType.ts, 19, 9))
}
}

0 comments on commit 60963d7

Please sign in to comment.