1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-22312] Resolve TS 5.8 errors (#16108)

* refactor: remove ts-strict-ignore and update

* refactor: remove ts-strict-ignore and update

* refactor: simplify if statement

* refactor: remove ts-strict-ignore and update

* refactor: add nullable union for interfaces
This commit is contained in:
Stephon Brown
2025-09-02 15:15:38 -04:00
committed by GitHub
parent 232dd89814
commit a4fca832f3
7 changed files with 105 additions and 75 deletions

View File

@@ -42,7 +42,7 @@ export type TrialOrganizationType = Exclude<ProductTierType, ProductTierType.Fre
export interface OrganizationInfo {
name: string;
email: string;
type: TrialOrganizationType;
type: TrialOrganizationType | null;
}
export interface OrganizationCreatedEvent {

View File

@@ -6,7 +6,7 @@
[showClose]="false"
*ngIf="freeTrialData?.shownBanner"
>
{{ freeTrialData.message }}
{{ freeTrialData?.message }}
<a
bitLink
linkType="secondary"

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Location } from "@angular/common";
import { Component, OnDestroy } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
@@ -47,21 +45,21 @@ import { FreeTrial } from "../../types/free-trial";
standalone: false,
})
export class OrganizationPaymentMethodComponent implements OnDestroy {
organizationId: string;
organizationId!: string;
isUnpaid = false;
accountCredit: number;
accountCredit?: number;
paymentSource?: PaymentSourceResponse;
subscriptionStatus?: string;
protected freeTrialData: FreeTrial;
organization: Organization;
organizationSubscriptionResponse: OrganizationSubscriptionResponse;
protected freeTrialData?: FreeTrial;
organization?: Organization;
organizationSubscriptionResponse?: OrganizationSubscriptionResponse;
loading = true;
protected readonly Math = Math;
launchPaymentModalAutomatically = false;
protected taxInformation: TaxInformation;
protected taxInformation?: TaxInformation;
constructor(
private activatedRoute: ActivatedRoute,
@@ -104,7 +102,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
.subscribe();
const state = this.router.getCurrentNavigation()?.extras?.state;
// incase the above state is undefined or null we use redundantState
// In case the above state is undefined or null, we use redundantState
const redundantState: any = location.getState();
const queryParam = this.activatedRoute.snapshot.queryParamMap.get(
"launchPaymentModalAutomatically",
@@ -116,10 +114,8 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
Object.prototype.hasOwnProperty.call(redundantState, "launchPaymentModalAutomatically")
) {
this.launchPaymentModalAutomatically = redundantState.launchPaymentModalAutomatically;
} else if (queryParam === "true") {
this.launchPaymentModalAutomatically = true;
} else {
this.launchPaymentModalAutomatically = false;
this.launchPaymentModalAutomatically = queryParam === "true";
}
}
ngOnDestroy(): void {
@@ -155,14 +151,21 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
this.paymentSource = paymentSource;
this.subscriptionStatus = subscriptionStatus;
this.taxInformation = taxInformation;
this.isUnpaid = this.subscriptionStatus === "unpaid";
if (this.organizationId) {
const organizationSubscriptionPromise = this.organizationApiService.getSubscription(
this.organizationId,
);
const userId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
if (!userId) {
throw new Error("User ID is not found");
}
const organizationPromise = await firstValueFrom(
this.organizationService
.organizations$(userId)
@@ -173,15 +176,20 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
organizationSubscriptionPromise,
organizationPromise,
]);
if (!this.organization) {
throw new Error("Organization is not found");
}
if (!this.paymentSource) {
throw new Error("Payment source is not found");
}
this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(
this.organization,
this.organizationSubscriptionResponse,
paymentSource,
this.paymentSource,
);
}
// TODO: Eslint upgrade. Please resolve this since the ?? does nothing
// eslint-disable-next-line no-constant-binary-expression
this.isUnpaid = this.subscriptionStatus === "unpaid" ?? false;
// If the flag `launchPaymentModalAutomatically` is set to true,
// we schedule a timeout (delay of 800ms) to automatically launch the payment modal.
// This delay ensures that any prior UI/rendering operations complete before triggering the modal.
@@ -219,14 +227,14 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
const dialogRef = TrialPaymentDialogComponent.open(this.dialogService, {
data: {
organizationId: this.organizationId,
subscription: this.organizationSubscriptionResponse,
productTierType: this.organization?.productTierType,
subscription: this.organizationSubscriptionResponse!,
productTierType: this.organization!.productTierType,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) {
this.location.replaceState(this.location.path(), "", {});
if (this.launchPaymentModalAutomatically && !this.organization.enabled) {
if (this.launchPaymentModalAutomatically && !this.organization?.enabled) {
await this.syncService.fullSync(true);
}
this.launchPaymentModalAutomatically = false;
@@ -238,13 +246,14 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
title: "",
message: this.i18nService.t("verifiedBankAccount"),
});
};
protected get accountCreditHeaderText(): string {
const key = this.accountCredit <= 0 ? "accountBalance" : "accountCredit";
const hasAccountCredit = this.accountCredit && this.accountCredit > 0;
const key = hasAccountCredit ? "accountCredit" : "accountBalance";
return this.i18nService.t(key);
}
@@ -279,7 +288,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
if (!hasBillingAddress) {
this.toastService.showToast({
variant: "error",
title: null,
title: "",
message: this.i18nService.t("billingAddressRequiredToAddCredit"),
});
return false;

View File

@@ -24,7 +24,7 @@ import {
import { PaymentComponent } from "../payment/payment.component";
export interface AdjustPaymentDialogParams {
initialPaymentMethod?: PaymentMethodType;
initialPaymentMethod?: PaymentMethodType | null;
organizationId?: string;
productTier?: ProductTierType;
providerId?: string;

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Location } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
@@ -42,21 +40,21 @@ import {
export class PaymentMethodComponent implements OnInit, OnDestroy {
loading = false;
firstLoaded = false;
billing: BillingPaymentResponse;
org: OrganizationSubscriptionResponse;
sub: SubscriptionResponse;
billing?: BillingPaymentResponse;
org?: OrganizationSubscriptionResponse;
sub?: SubscriptionResponse;
paymentMethodType = PaymentMethodType;
organizationId: string;
organizationId?: string;
isUnpaid = false;
organization: Organization;
organization?: Organization;
verifyBankForm = this.formBuilder.group({
amount1: new FormControl<number>(null, [
amount1: new FormControl<number>(0, [
Validators.required,
Validators.max(99),
Validators.min(0),
]),
amount2: new FormControl<number>(null, [
amount2: new FormControl<number>(0, [
Validators.required,
Validators.max(99),
Validators.min(0),
@@ -64,7 +62,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
});
launchPaymentModalAutomatically = false;
protected freeTrialData: FreeTrial;
protected freeTrialData?: FreeTrial;
constructor(
protected apiService: ApiService,
@@ -84,7 +82,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
private configService: ConfigService,
) {
const state = this.router.getCurrentNavigation()?.extras?.state;
// incase the above state is undefined or null we use redundantState
// In case the above state is undefined or null, we use redundantState
const redundantState: any = location.getState();
if (state && Object.prototype.hasOwnProperty.call(state, "launchPaymentModalAutomatically")) {
this.launchPaymentModalAutomatically = state.launchPaymentModalAutomatically;
@@ -129,17 +127,23 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
}
this.loading = true;
if (this.forOrganization) {
const billingPromise = this.organizationApiService.getBilling(this.organizationId);
const billingPromise = this.organizationApiService.getBilling(this.organizationId!);
const organizationSubscriptionPromise = this.organizationApiService.getSubscription(
this.organizationId,
this.organizationId!,
);
const userId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
if (!userId) {
throw new Error("User ID is not found");
}
const organizationPromise = await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(this.organizationId)),
.pipe(getOrganizationById(this.organizationId!)),
);
[this.billing, this.org, this.organization] = await Promise.all([
@@ -171,14 +175,16 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
};
addCredit = async () => {
const dialogRef = openAddCreditDialog(this.dialogService, {
data: {
organizationId: this.organizationId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AddCreditDialogResult.Added) {
await this.load();
if (this.forOrganization) {
const dialogRef = openAddCreditDialog(this.dialogService, {
data: {
organizationId: this.organizationId!,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AddCreditDialogResult.Added) {
await this.load();
}
}
};
@@ -194,7 +200,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
if (result === AdjustPaymentDialogResultType.Submitted) {
this.location.replaceState(this.location.path(), "", {});
if (this.launchPaymentModalAutomatically && !this.organization.enabled) {
if (this.launchPaymentModalAutomatically && !this.organization?.enabled) {
await this.syncService.fullSync(true);
}
this.launchPaymentModalAutomatically = false;
@@ -208,18 +214,22 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
}
const request = new VerifyBankRequest();
request.amount1 = this.verifyBankForm.value.amount1;
request.amount2 = this.verifyBankForm.value.amount2;
await this.organizationApiService.verifyBank(this.organizationId, request);
request.amount1 = this.verifyBankForm.value.amount1!;
request.amount2 = this.verifyBankForm.value.amount2!;
await this.organizationApiService.verifyBank(this.organizationId!, request);
this.toastService.showToast({
variant: "success",
title: null,
title: "",
message: this.i18nService.t("verifiedBankAccount"),
});
await this.load();
};
determineOrgsWithUpcomingPaymentIssues() {
if (!this.organization || !this.org || !this.billing) {
throw new Error("Organization, organization subscription, or billing is not defined");
}
this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(
this.organization,
this.org,

View File

@@ -37,7 +37,7 @@
bitButton
buttonType="primary"
[disabled]="orgInfoFormGroup.controls.name.invalid"
[loading]="loading && (trialPaymentOptional$ | async)"
[loading]="loading && (trialPaymentOptional$ | async)!"
(click)="orgNameEntrySubmit()"
>
{{
@@ -55,8 +55,8 @@
<app-trial-billing-step
*ngIf="stepper.selectedIndex === 2"
[organizationInfo]="{
name: orgInfoFormGroup.value.name,
email: orgInfoFormGroup.value.billingEmail,
name: orgInfoFormGroup.value.name!,
email: orgInfoFormGroup.value.billingEmail!,
type: trialOrganizationType,
}"
[subscriptionProduct]="

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { StepperSelectionEvent } from "@angular/cdk/stepper";
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
@@ -50,15 +48,15 @@ export type InitiationPath =
standalone: false,
})
export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
@ViewChild("stepper", { static: false }) verticalStepper!: VerticalStepperComponent;
inputPasswordFlow = InputPasswordFlow.SetInitialPasswordAccountRegistration;
initializing = true;
/** Password Manager or Secrets Manager */
product: ProductType;
product?: ProductType;
/** The tier of product being subscribed to */
productTier: ProductTierType;
productTier!: ProductTierType;
/** Product types that display steppers for Password Manager */
stepperProductTypes: ProductTierType[] = [
ProductTierType.Teams,
@@ -79,16 +77,16 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
orgId = "";
orgLabel = "";
billingSubLabel = "";
enforcedPolicyOptions: MasterPasswordPolicyOptions;
enforcedPolicyOptions?: MasterPasswordPolicyOptions;
/** User's email address associated with the trial */
email = "";
/** Token from the backend associated with the email verification */
emailVerificationToken: string;
emailVerificationToken?: string;
loading = false;
productTierValue: number;
productTierValue?: ProductTierType;
trialLength: number;
trialLength!: number;
orgInfoFormGroup = this.formBuilder.group({
name: ["", { validators: [Validators.required, Validators.maxLength(50)], updateOn: "change" }],
@@ -132,7 +130,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
// Show email validation toast when coming from email
if (qParams.fromEmail && qParams.fromEmail === "true") {
this.toastService.showToast({
title: null,
title: "",
message: this.i18nService.t("emailVerifiedV2"),
variant: "success",
});
@@ -172,9 +170,15 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
});
const invite = await this.organizationInviteService.getOrganizationInvite();
let policies: Policy[] | null = null;
let policies: Policy[] | undefined | null = null;
if (invite != null) {
if (
invite != null &&
invite.organizationId &&
invite.token &&
invite.email &&
invite.organizationUserId
) {
try {
policies = await this.policyApiService.getPoliciesByToken(
invite.organizationId,
@@ -218,7 +222,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
if (event.selectedIndex === 1 && this.orgInfoFormGroup.controls.name.value === "") {
this.orgInfoSubLabel = this.planInfoLabel;
} else if (event.previouslySelectedIndex === 1) {
this.orgInfoSubLabel = this.orgInfoFormGroup.controls.name.value;
this.orgInfoSubLabel = this.orgInfoFormGroup.controls.name.value!;
}
}
@@ -260,8 +264,11 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
}
const organization: OrganizationInformation = {
name: this.orgInfoFormGroup.value.name,
billingEmail: this.orgInfoFormGroup.value.billingEmail,
name: this.orgInfoFormGroup.value.name == null ? "" : this.orgInfoFormGroup.value.name,
billingEmail:
this.orgInfoFormGroup.value.billingEmail == null
? ""
: this.orgInfoFormGroup.value.billingEmail,
initiationPath: trialInitiationPath,
};
@@ -326,7 +333,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
}
}
get trialOrganizationType(): TrialOrganizationType {
get trialOrganizationType(): TrialOrganizationType | null {
if (this.productTier === ProductTierType.Free) {
return null;
}
@@ -352,8 +359,12 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
const response = await this.organizationBillingService.startFree({
organization: {
name: this.orgInfoFormGroup.value.name,
billingEmail: this.orgInfoFormGroup.value.billingEmail,
name: this.orgInfoFormGroup.value.name == null ? "" : this.orgInfoFormGroup.value.name,
billingEmail:
this.orgInfoFormGroup.value.billingEmail == null
? ""
: this.orgInfoFormGroup.value.billingEmail,
initiationPath: "Password Manager trial from marketing website",
},
plan: {
type: 0,
@@ -405,11 +416,11 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
await this.loginStrategyService.logIn(credentials);
}
finishRegistration(passwordInputResult: PasswordInputResult) {
async finishRegistration(passwordInputResult: PasswordInputResult) {
this.submitting = true;
return this.registrationFinishService
.finishRegistration(this.email, passwordInputResult, this.emailVerificationToken)
.catch((e) => {
.catch((e: unknown): null => {
this.validationService.showError(e);
this.submitting = false;
return null;