Add tests

This commit is contained in:
Anders Hejlsberg
2019-09-20 17:17:39 -07:00
parent cafec556f3
commit 21b5418cef
5 changed files with 564 additions and 0 deletions

View File

@@ -40,3 +40,17 @@ function foo5(bar: "a" | "b"): number {
return 1;
}
}
function foo6(bar: "a", a: boolean, b: boolean): number {
if (a) {
switch (bar) {
case "a": return 1;
}
}
else {
switch (b) {
case true: return -1;
case false: return 0;
}
}
}

View File

@@ -0,0 +1,128 @@
// @strict: true
// @declaration: true
declare function isString(value: unknown): value is string;
declare function isArrayOfStrings(value: unknown): value is string[];
const assert: (value: unknown) => asserts value = value => {}
declare function assertIsString(value: unknown): asserts value is string;
declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
function f01(x: unknown) {
if (!!true) {
assert(typeof x === "string");
x.length;
}
if (!!true) {
assert(x instanceof Error);
x.message;
}
if (!!true) {
assert(typeof x === "boolean" || typeof x === "number");
x.toLocaleString;
}
if (!!true) {
assert(isArrayOfStrings(x));
x[0].length;
}
if (!!true) {
assertIsArrayOfStrings(x);
x[0].length;
}
if (!!true) {
assert(x === undefined || typeof x === "string");
x; // string | undefined
assertDefined(x);
x; // string
}
}
function f02(x: string | undefined) {
if (!!true) {
assert(x);
x.length;
}
if (!!true) {
assert(x !== undefined);
x.length;
}
if (!!true) {
assertDefined(x);
x.length;
}
}
function f03(x: string | undefined, assert: (value: unknown) => asserts value) {
assert(x);
x.length;
}
namespace Debug {
export declare function assert(value: unknown, message?: string): asserts value;
export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
}
function f10(x: string | undefined) {
if (!!true) {
Debug.assert(x);
x.length;
}
if (!!true) {
Debug.assert(x !== undefined);
x.length;
}
if (!!true) {
Debug.assertDefined(x);
x.length;
}
}
class Test {
assert(value: unknown): asserts value {
if (value) return;
throw new Error();
}
isTest2(): this is Test2 {
return this instanceof Test2;
}
assertIsTest2(): asserts this is Test2 {
if (this instanceof Test2) return;
throw new Error();
}
assertThis(): asserts this {
if (!this) return;
throw new Error();
}
bar() {
this.assertThis();
this;
}
foo(x: unknown) {
this.assert(typeof x === "string");
x.length;
if (this.isTest2()) {
this.z;
}
this.assertIsTest2();
this.z;
}
}
class Test2 extends Test {
z = 0;
}
// Invalid constructs
declare let Q1: new (x: unknown) => x is string;
declare let Q2: new (x: boolean) => asserts x;
declare let Q3: new (x: unknown) => asserts x is string;
declare class Wat {
get p1(): this is string;
set p1(x: this is string);
get p2(): asserts this is string;
set p2(x: asserts this is string);
}

View File

@@ -0,0 +1,199 @@
// @strict: true
// @allowUnreachableCode: false
// @declaration: true
function f1(x: 1 | 2): string {
if (!!true) {
switch (x) {
case 1: return 'a';
case 2: return 'b';
}
x; // Unreachable
}
else {
throw 0;
}
}
function f2(x: 1 | 2) {
let z: number;
switch (x) {
case 1: z = 10; break;
case 2: z = 20; break;
}
z; // Definitely assigned
}
function f3(x: 1 | 2) {
switch (x) {
case 1: return 10;
case 2: return 20;
// Default considered reachable to allow defensive coding
default: throw new Error("Bad input");
}
}
// Repro from #11572
enum E { A, B }
function f(e: E): number {
switch (e) {
case E.A: return 0
case E.B: return 1
}
}
function g(e: E): number {
if (!true)
return -1
else
switch (e) {
case E.A: return 0
case E.B: return 1
}
}
// Repro from #12668
interface Square { kind: "square"; size: number; }
interface Rectangle { kind: "rectangle"; width: number; height: number; }
interface Circle { kind: "circle"; radius: number; }
interface Triangle { kind: "triangle"; side: number; }
type Shape = Square | Rectangle | Circle | Triangle;
function area(s: Shape): number {
let area;
switch (s.kind) {
case "square": area = s.size * s.size; break;
case "rectangle": area = s.width * s.height; break;
case "circle": area = Math.PI * s.radius * s.radius; break;
case "triangle": area = Math.sqrt(3) / 4 * s.side * s.side; break;
}
return area;
}
function areaWrapped(s: Shape): number {
let area;
area = (() => {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius * s.radius;
case "triangle": return Math.sqrt(3) / 4 * s.side * s.side;
}
})();
return area;
}
// Repro from #13241
enum MyEnum {
A,
B
}
function thisGivesError(e: MyEnum): string {
let s: string;
switch (e) {
case MyEnum.A: s = "it was A"; break;
case MyEnum.B: s = "it was B"; break;
}
return s;
}
function good1(e: MyEnum): string {
let s: string;
switch (e) {
case MyEnum.A: s = "it was A"; break;
case MyEnum.B: s = "it was B"; break;
default: s = "it was something else"; break;
}
return s;
}
function good2(e: MyEnum): string {
switch (e) {
case MyEnum.A: return "it was A";
case MyEnum.B: return "it was B";
}
}
// Repro from #18362
enum Level {
One,
Two,
}
const doSomethingWithLevel = (level: Level) => {
let next: Level;
switch (level) {
case Level.One:
next = Level.Two;
break;
case Level.Two:
next = Level.One;
break;
}
return next;
};
// Repro from #20409
interface Square2 {
kind: "square";
size: number;
}
interface Circle2 {
kind: "circle";
radius: number;
}
type Shape2 = Square2 | Circle2;
function withDefault(s1: Shape2, s2: Shape2): string {
switch (s1.kind) {
case "square":
return "1";
case "circle":
switch (s2.kind) {
case "square":
return "2";
case "circle":
return "3";
default:
return "never";
}
}
}
function withoutDefault(s1: Shape2, s2: Shape2): string {
switch (s1.kind) {
case "square":
return "1";
case "circle":
switch (s2.kind) {
case "square":
return "2";
case "circle":
return "3";
}
}
}
// Repro from #20823
function test4(value: 1 | 2) {
let x: string;
switch (value) {
case 1: x = "one"; break;
case 2: x = "two"; break;
}
return x;
}

View File

@@ -0,0 +1,158 @@
// @strict: true
// @allowUnreachableCode: false
// @declaration: true
function fail(message?: string): never {
throw new Error(message);
}
function f01(x: string | undefined) {
if (x === undefined) fail("undefined argument");
x.length; // string
}
function f02(x: number): number {
if (x >= 0) return x;
fail("negative number");
x; // Unreachable
}
function f03(x: string) {
x; // string
fail();
x; // Unreachable
}
function f11(x: string | undefined, fail: (message?: string) => never) {
if (x === undefined) fail("undefined argument");
x.length; // string
}
function f12(x: number, fail: (message?: string) => never): number {
if (x >= 0) return x;
fail("negative number");
x; // Unreachable
}
function f13(x: string, fail: (message?: string) => never) {
x; // string
fail();
x; // Unreachable
}
namespace Debug {
export declare function fail(message?: string): never;
}
function f21(x: string | undefined) {
if (x === undefined) Debug.fail("undefined argument");
x.length; // string
}
function f22(x: number): number {
if (x >= 0) return x;
Debug.fail("negative number");
x; // Unreachable
}
function f23(x: string) {
x; // string
Debug.fail();
x; // Unreachable
}
function f24(x: string) {
x; // string
((Debug).fail)();
x; // Unreachable
}
class Test {
fail(message?: string): never {
throw new Error(message);
}
f1(x: string | undefined) {
if (x === undefined) this.fail("undefined argument");
x.length; // string
}
f2(x: number): number {
if (x >= 0) return x;
this.fail("negative number");
x; // Unreachable
}
f3(x: string) {
x; // string
this.fail();
x; // Unreachable
}
}
function f30(x: string | number | undefined) {
if (typeof x === "string") {
fail();
x; // Unreachable
}
else {
x; // number | undefined
if (x !== undefined) {
x; // number
fail();
x; // Unreachable
}
else {
x; // undefined
fail();
x; // Unreachable
}
x; // Unreachable
}
x; // Unreachable
}
function f31(x: { a: string | number }) {
if (typeof x.a === "string") {
fail();
x; // Unreachable
x.a; // Unreachable
}
x; // { a: string | number }
x.a; // number
}
function f40(x: number) {
try {
x;
fail();
x; // Unreachable
}
finally {
x;
fail();
x; // Unreachable
}
x; // Unreachable
}
function f41(x: number) {
try {
x;
}
finally {
x;
fail();
x; // Unreachable
}
x; // Unreachable
}
function f42(x: number) {
try {
x;
fail();
x; // Unreachable
}
finally {
x;
}
x; // Unreachable
}

View File

@@ -0,0 +1,65 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @allowUnreachableCode: false
// @filename: assertionsAndNonReturningFunctions.js
/** @typedef {(check: boolean) => asserts check} AssertFunc */
/** @type {AssertFunc} */
const assert = check => {
if (!check) throw new Error();
}
/** @type {(x: unknown) => asserts x is string } */
function assertIsString(x) {
if (!(typeof x === "string")) throw new Error();
}
/**
* @param {boolean} check
* @returns {asserts check}
*/
function assert2(check) {
if (!check) throw new Error();
}
/**
* @returns {never}
*/
function fail() {
throw new Error();
}
/**
* @param {*} x
*/
function f1(x) {
if (!!true) {
assert(typeof x === "string");
x.length;
}
if (!!true) {
assert2(typeof x === "string");
x.length;
}
if (!!true) {
assertIsString(x);
x.length;
}
if (!!true) {
fail();
x; // Unreachable
}
}
/**
* @param {boolean} b
*/
function f2(b) {
switch (b) {
case true: return 1;
case false: return 0;
}
b; // Unreachable
}