/* * Copyright 2015 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.newtypes.JSTypeCreatorFromJSDoc; /** * Tests for the new type inference on transpiled code that includes * type annotations in the language syntax. * * <p>We will eventually type check it natively, without transpiling. * * @author dimvar@google.com (Dimitris Vardoulakis) */ public final class NewTypeInferenceWithTypeSyntaxTranspilationTest extends NewTypeInferenceTestBase { @Override protected void setUp() throws Exception { super.setUp(); compilerOptions.setLanguageIn(LanguageMode.ECMASCRIPT6_TYPED); compilerOptions.setLanguageOut(LanguageMode.ECMASCRIPT3); } public void testSimpleAnnotationsNoWarnings() { typeCheck("var x: number = 123;"); typeCheck("var x: string = 'adsf';"); typeCheck("var x: boolean = true;"); typeCheck("var x: number[] = [1, 2, 3];"); typeCheck("function f(): void { return undefined; }"); typeCheck(LINE_JOINER.join( "class Foo {}", "var x: Foo = new Foo;")); typeCheck("var x: {p: string; q: number};"); typeCheck("type Foo = number; var x: Foo = 3;"); } public void testSimpleAnnotationsWarnings() { typeCheck("var x: number[] = ['hello'];", NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck("var x: {p: string; q: number}; x = {p: 3, q: 3}", NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck("type Foo = number; var x: Foo = '3';", NewTypeInference.MISTYPED_ASSIGN_RHS); } public void testSimpleFunctions() { typeCheck(LINE_JOINER.join( "function f(x: number) {}", "f(123);")); typeCheck(LINE_JOINER.join( "function f(x: number) {}", "f('asdf');"), NewTypeInference.INVALID_ARGUMENT_TYPE); typeCheck(LINE_JOINER.join( "function f(x): string { return x; }", "f(123);"), NewTypeInference.INVALID_ARGUMENT_TYPE); } public void testSimpleClasses() { typeCheck(LINE_JOINER.join( "class Foo {}", // Nominal types are non-nullable by default "var x: Foo = null;"), NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck(LINE_JOINER.join( "class Foo {}", "class Bar {}", "var x: Bar = new Foo;"), NewTypeInference.MISTYPED_ASSIGN_RHS); } public void testClassPropertyDeclarations() { typeCheck(LINE_JOINER.join( "class Foo {", " prop: number;", " constructor() { this.prop = 'asdf'; }", "}"), NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck(LINE_JOINER.join( "class Foo {", " prop: string;", "}", "(new Foo).prop - 5;"), NewTypeInference.INVALID_OPERAND_TYPE); typeCheck(LINE_JOINER.join( "class Foo {", " static prop: number;", "}", "Foo.prop = 'asdf';"), NewTypeInference.MISTYPED_ASSIGN_RHS); // TODO(dimvar): up to ES5, prop decls use dot. // Should we start allowing [] for @unrestricted classes? typeCheck(LINE_JOINER.join( "/** @unrestricted */ class Foo {", " ['prop']: string;", "}", "(new Foo).prop - 5;"), NewTypeInference.INEXISTENT_PROPERTY); } public void testOptionalParameter() { typeCheck(LINE_JOINER.join( "function foo(p1?: string) {}", "foo(); foo('str');")); typeCheck(LINE_JOINER.join( "function foo(p0, p1?: string) {}", "foo('2', 3)"), NewTypeInference.INVALID_ARGUMENT_TYPE); } public void testRestParameter() { typeCheck(LINE_JOINER.join( "function foo(...p1: number[]) {}", "foo(); foo(3); foo(3, 4);")); typeCheck(LINE_JOINER.join( "function foo(...p1: number[]) {}", "foo('3')"), NewTypeInference.INVALID_ARGUMENT_TYPE); typeCheck("function foo(...p1: number[]) { var s:string = p1[0]; }", NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck("function foo(...p1: number[]) { p1 = ['3']; }", NewTypeInference.MISTYPED_ASSIGN_RHS); } public void testClass() { typeCheck(LINE_JOINER.join( "class Foo {", " prop: number;", "}", "class Bar extends Foo {", "}", "(new Bar).prop = '3'"), NewTypeInference.MISTYPED_ASSIGN_RHS); typeCheck( "class Foo extends Foo {}", JSTypeCreatorFromJSDoc.INHERITANCE_CYCLE, NewTypeInference.UNDEFINED_SUPER_CLASS); } public void testInterface() { typeCheck(LINE_JOINER.join( "interface Foo {}", "(new Foo);"), NewTypeInference.NOT_A_CONSTRUCTOR); typeCheck(LINE_JOINER.join( "interface Foo {", " prop: number;", "}", "class Bar implements Foo {", "}"), GlobalTypeInfo.INTERFACE_METHOD_NOT_IMPLEMENTED); typeCheck(LINE_JOINER.join( "interface Foo {", " prop: number;", "}", "class Bar extends Foo {", "}"), JSTypeCreatorFromJSDoc.CONFLICTING_EXTENDED_TYPE); typeCheck("interface Foo extends Foo {}", JSTypeCreatorFromJSDoc.INHERITANCE_CYCLE); typeCheck(LINE_JOINER.join( "interface Foo {", " prop: number;", "}", "interface Bar {", " prop: string;", "}", "interface Baz extends Foo, Bar {}"), GlobalTypeInfo.SUPER_INTERFACES_HAVE_INCOMPATIBLE_PROPERTIES); } public void testAmbientDeclarationsInCode() { typeCheck("declare var x: number;", Es6TypedToEs6Converter.DECLARE_IN_NON_EXTERNS); typeCheck("declare function f(): void;", Es6TypedToEs6Converter.DECLARE_IN_NON_EXTERNS); typeCheck("declare class C { constructor(); }", Es6TypedToEs6Converter.DECLARE_IN_NON_EXTERNS); typeCheck("declare enum Foo { BAR }", Es6TypedToEs6Converter.DECLARE_IN_NON_EXTERNS); } public void testGetterReturnNonDeclaredType() { // getters cannot be transpiled to EC3 compilerOptions.setLanguageOut(LanguageMode.ECMASCRIPT5); typeCheck( "var x = {get a(): number { return 'str'; }}", NewTypeInference.RETURN_NONDECLARED_TYPE); } }