mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
dfe7be4d2a
The static `ɵprov` field emitted on `@Injectable()` classes uses `ɵɵInjectableDeclaration<T>`.
When a subclass extends a generic `@Injectable()` base class with contravariant parameters
(such as callback/transformer methods depending on generic type parameters), TypeScript's
static side inheritance check (`typeof Sub extends typeof Super`) fails with `TS2417` because
`ɵɵInjectableDeclaration<Sub>` is not assignable to `ɵɵInjectableDeclaration<Super<any>>`.
Using `any` (`o.DYNAMIC_TYPE`) in `createInjectableType` avoids strict variance checks on
static inheritance for internal Ivy definitions and aligns with other Ivy declaration types.
(cherry picked from commit caeab598c9)
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
/*!
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
|
|
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
|
|
import {loadStandardTestFiles} from '../../src/ngtsc/testing';
|
|
import {NgtscTestEnvironment} from './env';
|
|
|
|
const testFiles = loadStandardTestFiles();
|
|
|
|
runInEachFileSystem(() => {
|
|
describe('@Service decorator', () => {
|
|
let env!: NgtscTestEnvironment;
|
|
|
|
beforeEach(() => {
|
|
env = NgtscTestEnvironment.setup(testFiles);
|
|
env.tsconfig();
|
|
});
|
|
|
|
// More thorough compilation tests are in `packages/compiler-cli/test/compliance/test_cases/service_decorator`.
|
|
it('should compile an @Service class', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Service} from '@angular/core';
|
|
|
|
@Service()
|
|
export class TestService {}
|
|
`,
|
|
);
|
|
env.driveMain();
|
|
|
|
const jsContents = env.getContents('test.js');
|
|
const dtsContents = env.getContents('test.d.ts');
|
|
expect(jsContents).not.toContain('__decorate');
|
|
expect(jsContents).toContain('TestService.ɵfac =');
|
|
expect(jsContents).toContain('TestService.ɵprov =');
|
|
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDeclaration<TestService, never>;');
|
|
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDeclaration<any>;');
|
|
});
|
|
|
|
it('should report if an @Service class has another Angular decorator', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Service, Pipe} from '@angular/core';
|
|
|
|
@Service()
|
|
@Pipe({name: 'foo'})
|
|
export class TestService {}
|
|
`,
|
|
);
|
|
|
|
const diags = env.driveDiagnostics();
|
|
expect(diags.length).toBe(1);
|
|
expect(diags[0].messageText).toBe(
|
|
'Cannot apply more than one Angular decorator on an @Service class.',
|
|
);
|
|
});
|
|
|
|
it('should report if an @Service class uses constructor-based DI from its own constructor', () => {
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Service, ApplicationRef} from '@angular/core';
|
|
|
|
@Service()
|
|
export class TestService {
|
|
constructor(appRef: ApplicationRef) {}
|
|
}
|
|
`,
|
|
);
|
|
|
|
const diags = env.driveDiagnostics();
|
|
expect(diags.length).toBe(1);
|
|
expect(diags[0].messageText).toBe(
|
|
'@Service class cannot use constructor dependency injection. Use the `inject` function instead.',
|
|
);
|
|
});
|
|
|
|
it('should report if an @Service class uses constructor-based DI from an inherited constructor', () => {
|
|
env.write(
|
|
'grandparent.ts',
|
|
`
|
|
import {Injectable, ApplicationRef} from '@angular/core';
|
|
|
|
@Injectable()
|
|
export class GrandparentService {
|
|
constructor(appRef: ApplicationRef) {}
|
|
}
|
|
`,
|
|
);
|
|
|
|
env.write(
|
|
'parent.ts',
|
|
`
|
|
import {Injectable} from '@angular/core';
|
|
import {GrandparentService} from './grandparent';
|
|
|
|
@Injectable()
|
|
export class ParentService extends GrandparentService {}
|
|
`,
|
|
);
|
|
|
|
env.write(
|
|
'test.ts',
|
|
`
|
|
import {Service} from '@angular/core';
|
|
import {ParentService} from './parent';
|
|
|
|
@Service()
|
|
export class TestService extends ParentService {}
|
|
`,
|
|
);
|
|
|
|
const diags = env.driveDiagnostics();
|
|
expect(diags.length).toBe(1);
|
|
expect(diags[0].messageText).toBe(
|
|
'@Service class cannot use constructor dependency injection. Use the `inject` function instead.',
|
|
);
|
|
});
|
|
});
|
|
});
|