fix: resolve TypeScript type errors across codebase

- Add experimentalDecorators support in tsconfig.node.json for TypeORM entities
- Fix mssql module import in order-resolver.ts (static vs dynamic import)
- Extend ISqlType parameter types in sql-server.ts for NVarChar compatibility
- Fix variable naming and type assertions in bip-users-dao.ts
- Add proper type assertions for IPC call results in renderer hooks
  (useAuth, useCleaner, useExtractor, useValidation)
- Add definite assignment assertions in config-manager.ts
This commit is contained in:
Misaka_Company
2026-03-04 14:03:23 +08:00
parent 5497e86b58
commit 88c8c256e2
9 changed files with 118 additions and 61 deletions

View File

@@ -147,8 +147,8 @@ function validateEditableFields(settings: Partial<SettingsData>): {
*/ */
export class ConfigManager { export class ConfigManager {
private static instance: ConfigManager | null = null private static instance: ConfigManager | null = null
private envPath: string private envPath!: string
private backupPath: string private backupPath!: string
private configCache: Map<string, string> = new Map() private configCache: Map<string, string> = new Map()
private initialized: boolean = false private initialized: boolean = false
@@ -214,6 +214,8 @@ export class ConfigManager {
* @param key - Configuration key * @param key - Configuration key
* @param defaultValue - Default value if key doesn't exist * @param defaultValue - Default value if key doesn't exist
*/ */
public get(key: string): string | undefined
public get(key: string, defaultValue: string): string
public get(key: string, defaultValue?: string): string | undefined { public get(key: string, defaultValue?: string): string | undefined {
return this.configCache.get(key) ?? defaultValue return this.configCache.get(key) ?? defaultValue
} }

View File

@@ -114,7 +114,13 @@ export class SqlServerService implements IDatabaseService {
*/ */
async queryWithParams( async queryWithParams(
sqlString: string, sqlString: string,
params: Record<string, { value: unknown; type?: sql.ISqlType }> params: Record<
string,
{
value: unknown
type?: sql.ISqlType | sql.ISqlTypeFactoryWithLength | sql.ISqlTypeWithLength
}
>
): Promise<QueryResult> { ): Promise<QueryResult> {
if (!this.pool) { if (!this.pool) {
throw new Error('Not connected to SQL Server. Call connect() first.') throw new Error('Not connected to SQL Server. Call connect() first.')

View File

@@ -14,18 +14,10 @@
import type { IDatabaseService } from '../database' import type { IDatabaseService } from '../database'
import { SqlServerService } from '../database/sql-server' import { SqlServerService } from '../database/sql-server'
import { createLogger } from '../logger' import { createLogger } from '../logger'
import sql from 'mssql'
const log = createLogger('OrderResolver') const log = createLogger('OrderResolver')
// Dynamically import mssql for SQL Server parameter types
let mssql: typeof import('mssql') | null = null
async function getMssql() {
if (!mssql) {
mssql = await import('mssql')
}
return mssql
}
/** /**
* Order mapping result * Order mapping result
*/ */
@@ -251,18 +243,26 @@ export class OrderNumberResolver {
const isSqlServer = this.dbService.type === 'sqlserver' const isSqlServer = this.dbService.type === 'sqlserver'
const tableName = this.getTableName(DB_CONFIG.TABLE_NAME) const tableName = this.getTableName(DB_CONFIG.TABLE_NAME)
log.debug('Resolving production IDs', { count: productionIds.length, dbType: this.dbService.type }) log.debug('Resolving production IDs', {
count: productionIds.length,
dbType: this.dbService.type
})
let result let result
if (isSqlServer) { if (isSqlServer) {
// Use queryWithParams for SQL Server with explicit parameter types // Use queryWithParams for SQL Server with explicit parameter types
const sql = await getMssql()
const placeholders = productionIds.map((_, idx) => `@p${idx}`).join(', ') const placeholders = productionIds.map((_, idx) => `@p${idx}`).join(', ')
const params: Record<string, { value: string; type: sql.ISqlType }> = {} const params: Record<
string,
{
value: string
type: sql.ISqlType | sql.ISqlTypeFactoryWithLength | sql.ISqlTypeWithLength
}
> = {}
productionIds.forEach((id, idx) => { productionIds.forEach((id, idx) => {
params[`p${idx}`] = { value: id, type: sql.NVarChar } params[`p${idx}`] = { value: id, type: sql.NVarChar(255) }
}) })
const query = ` const query = `
@@ -351,12 +351,17 @@ export class OrderNumberResolver {
if (isSqlServer) { if (isSqlServer) {
// Use queryWithParams for SQL Server with explicit parameter types // Use queryWithParams for SQL Server with explicit parameter types
const sql = await getMssql()
const placeholders = orderNumbers.map((_, idx) => `@p${idx}`).join(', ') const placeholders = orderNumbers.map((_, idx) => `@p${idx}`).join(', ')
const params: Record<string, { value: string; type: sql.ISqlType }> = {} const params: Record<
string,
{
value: string
type: sql.ISqlType | sql.ISqlTypeFactoryWithLength | sql.ISqlTypeWithLength
}
> = {}
orderNumbers.forEach((id, idx) => { orderNumbers.forEach((id, idx) => {
params[`p${idx}`] = { value: id, type: sql.NVarChar } params[`p${idx}`] = { value: id, type: sql.NVarChar(255) }
}) })
const query = ` const query = `

View File

@@ -121,8 +121,8 @@ export class BIPUsersDAO {
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await (dbService as SqlServerService).queryWithParams(sqlString, {
username: { value: username, type: sql.NVarChar }, username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar } password: { value: password, type: sql.NVarChar(255) }
}) })
if (result.rows.length > 0) { if (result.rows.length > 0) {
@@ -177,7 +177,7 @@ export class BIPUsersDAO {
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await (dbService as SqlServerService).queryWithParams(sqlString, {
computerName: { value: computerName, type: sql.NVarChar } computerName: { value: computerName, type: sql.NVarChar(255) }
}) })
if (result.rows.length > 0) { if (result.rows.length > 0) {
@@ -265,49 +265,55 @@ export class BIPUsersDAO {
const tableName = this.getTableName() const tableName = this.getTableName()
if (this.dbType === 'sqlserver') { if (this.dbType === 'sqlserver') {
let sql: string let sqlString: string
let params: Record<string, { value: unknown; type?: sql.ISqlType }> let params: Record<
string,
{
value: unknown
type?: sql.ISqlType | sql.ISqlTypeFactoryWithLength | sql.ISqlTypeWithLength
}
>
if (computerName) { if (computerName) {
sql = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerNmae) (UserName, Password, UserType, ComputerNmae)
VALUES (@username, @password, @userType, @computerName) VALUES (@username, @password, @userType, @computerName)
` `
params = { params = {
username: { value: username, type: sql.NVarChar }, username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar }, password: { value: password, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar }, userType: { value: userType, type: sql.NVarChar(255) },
computerName: { value: computerName, type: sql.NVarChar } computerName: { value: computerName, type: sql.NVarChar(255) }
} }
} else { } else {
sql = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType) (UserName, Password, UserType)
VALUES (@username, @password, @userType) VALUES (@username, @password, @userType)
` `
params = { params = {
username: { value: username, type: sql.NVarChar }, username: { value: username, type: sql.NVarChar(255) },
password: { value: password, type: sql.NVarChar }, password: { value: password, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar } userType: { value: userType, type: sql.NVarChar(255) }
} }
} }
await (dbService as SqlServerService).queryWithParams(sqlString, params) await (dbService as SqlServerService).queryWithParams(sqlString, params)
return true return true
} else { } else {
let sql: string let sqlString: string
let params: any[] let params: unknown[]
if (computerName) { if (computerName) {
sql = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType, ComputerNmae) (UserName, Password, UserType, ComputerNmae)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
` `
params = [username, password, userType, computerName] params = [username, password, userType, computerName]
} else { } else {
sql = ` sqlString = `
INSERT INTO ${tableName} INSERT INTO ${tableName}
(UserName, Password, UserType) (UserName, Password, UserType)
VALUES (?, ?, ?) VALUES (?, ?, ?)
@@ -343,8 +349,8 @@ export class BIPUsersDAO {
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await (dbService as SqlServerService).queryWithParams(sqlString, {
username: { value: username, type: sql.NVarChar }, username: { value: username, type: sql.NVarChar(255) },
userType: { value: userType, type: sql.NVarChar } userType: { value: userType, type: sql.NVarChar(255) }
}) })
return true return true
} else { } else {
@@ -382,8 +388,8 @@ export class BIPUsersDAO {
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await (dbService as SqlServerService).queryWithParams(sqlString, {
username: { value: username, type: sql.NVarChar }, username: { value: username, type: sql.NVarChar(255) },
newPassword: { value: newPassword, type: sql.NVarChar } newPassword: { value: newPassword, type: sql.NVarChar(255) }
}) })
return true return true
} else { } else {
@@ -419,7 +425,7 @@ export class BIPUsersDAO {
` `
await (dbService as SqlServerService).queryWithParams(sqlString, { await (dbService as SqlServerService).queryWithParams(sqlString, {
username: { value: username, type: sql.NVarChar } username: { value: username, type: sql.NVarChar(255) }
}) })
return true return true
} else { } else {
@@ -455,7 +461,7 @@ export class BIPUsersDAO {
` `
const result = await (dbService as SqlServerService).queryWithParams(sqlString, { const result = await (dbService as SqlServerService).queryWithParams(sqlString, {
username: { value: username, type: sql.NVarChar } username: { value: username, type: sql.NVarChar(255) }
}) })
return result.rows.length > 0 && (result.rows[0].count as number) > 0 return result.rows.length > 0 && (result.rows[0].count as number) > 0
} else { } else {

View File

@@ -53,7 +53,11 @@ export function useAuth(): UseAuthReturn {
setState((prev) => ({ ...prev, loading: true, error: null })) setState((prev) => ({ ...prev, loading: true, error: null }))
try { try {
const result = await window.electron.ipcRenderer.invoke('auth:login', credentials) const result = (await window.electron.ipcRenderer.invoke('auth:login', credentials)) as {
success: boolean
userInfo?: UserInfo
error?: string
}
if (result.success && result.userInfo) { if (result.success && result.userInfo) {
setState({ setState({
@@ -85,7 +89,12 @@ export function useAuth(): UseAuthReturn {
setState((prev) => ({ ...prev, loading: true, error: null })) setState((prev) => ({ ...prev, loading: true, error: null }))
try { try {
const result = await window.electron.ipcRenderer.invoke('auth:silentLogin') const result = (await window.electron.ipcRenderer.invoke('auth:silentLogin')) as {
success: boolean
userInfo?: UserInfo
error?: string
requiresUserSelection?: boolean
}
if (result.success && result.userInfo) { if (result.success && result.userInfo) {
setState({ setState({
@@ -126,14 +135,18 @@ export function useAuth(): UseAuthReturn {
const getCurrentUser = useCallback(async (): Promise<UserInfo | null> => { const getCurrentUser = useCallback(async (): Promise<UserInfo | null> => {
try { try {
const result = await window.electron.ipcRenderer.invoke('auth:getCurrentUser') const result = (await window.electron.ipcRenderer.invoke('auth:getCurrentUser')) as {
isAuthenticated: boolean
userInfo?: UserInfo
}
if (result.isAuthenticated && result.userInfo) { if (result.isAuthenticated && result.userInfo) {
const userInfo = result.userInfo
setState((prev) => ({ setState((prev) => ({
...prev, ...prev,
user: result.userInfo, user: userInfo,
isAuthenticated: true isAuthenticated: true
})) }))
return result.userInfo return userInfo
} }
return null return null
} catch { } catch {
@@ -143,7 +156,7 @@ export function useAuth(): UseAuthReturn {
const getAllUsers = useCallback(async (): Promise<UserInfo[]> => { const getAllUsers = useCallback(async (): Promise<UserInfo[]> => {
try { try {
return await window.electron.ipcRenderer.invoke('auth:getAllUsers') return (await window.electron.ipcRenderer.invoke('auth:getAllUsers')) as UserInfo[]
} catch { } catch {
return [] return []
} }
@@ -153,7 +166,11 @@ export function useAuth(): UseAuthReturn {
setState((prev) => ({ ...prev, loading: true, error: null })) setState((prev) => ({ ...prev, loading: true, error: null }))
try { try {
const result = await window.electron.ipcRenderer.invoke('auth:switchUser', userInfo) const result = (await window.electron.ipcRenderer.invoke('auth:switchUser', userInfo)) as {
success: boolean
userInfo?: UserInfo
error?: string
}
if (result.success && result.userInfo) { if (result.success && result.userInfo) {
setState({ setState({
@@ -180,7 +197,7 @@ export function useAuth(): UseAuthReturn {
const isAdmin = useCallback(async (): Promise<boolean> => { const isAdmin = useCallback(async (): Promise<boolean> => {
try { try {
return await window.electron.ipcRenderer.invoke('auth:isAdmin') return (await window.electron.ipcRenderer.invoke('auth:isAdmin')) as boolean
} catch { } catch {
return false return false
} }

View File

@@ -44,9 +44,13 @@ export function useCleaner(): UseCleanerReturn {
setState({ loading: true, data: null, error: null }) setState({ loading: true, data: null, error: null })
try { try {
const result = await window.electron.ipcRenderer.invoke('cleaner:run', input) const result = (await window.electron.ipcRenderer.invoke('cleaner:run', input)) as {
success: boolean
data?: CleanerResult
error?: string
}
if (result.success) { if (result.success && result.data) {
setState({ loading: false, data: result.data, error: null }) setState({ loading: false, data: result.data, error: null })
return result.data return result.data
} else { } else {

View File

@@ -43,9 +43,13 @@ export function useExtractor(): UseExtractorReturn {
setState({ loading: true, data: null, error: null }) setState({ loading: true, data: null, error: null })
try { try {
const result = await window.electron.ipcRenderer.invoke('extractor:run', input) const result = (await window.electron.ipcRenderer.invoke('extractor:run', input)) as {
success: boolean
data?: ExtractorResult
error?: string
}
if (result.success) { if (result.success && result.data) {
setState({ loading: false, data: result.data, error: null }) setState({ loading: false, data: result.data, error: null })
return result.data return result.data
} else { } else {

View File

@@ -68,13 +68,18 @@ export function useValidation(): UseValidationReturn {
setState((prev) => ({ ...prev, loading: true, error: null })) setState((prev) => ({ ...prev, loading: true, error: null }))
try { try {
const result = await window.electron.ipcRenderer.invoke('validation:validate', request) const result = (await window.electron.ipcRenderer.invoke(
'validation:validate',
request
)) as ValidationResponse
if (result.success) { if (result.success) {
const results = result.results || null
const stats = result.stats || null
setState({ setState({
loading: false, loading: false,
data: result.results || null, data: results,
stats: result.stats || null, stats: stats,
error: null error: null
}) })
return result return result
@@ -105,7 +110,9 @@ export function useValidation(): UseValidationReturn {
const getSharedProductionIds = useCallback(async (): Promise<string[]> => { const getSharedProductionIds = useCallback(async (): Promise<string[]> => {
try { try {
const result = await window.electron.ipcRenderer.invoke('validation:getSharedProductionIds') const result = (await window.electron.ipcRenderer.invoke(
'validation:getSharedProductionIds'
)) as { productionIds?: string[] }
return result?.productionIds || [] return result?.productionIds || []
} catch { } catch {
return [] return []
@@ -117,7 +124,11 @@ export function useValidation(): UseValidationReturn {
materialCodes: string[] materialCodes: string[]
} | null> => { } | null> => {
try { try {
const result = await window.electron.ipcRenderer.invoke('validation:getCleanerData') const result = (await window.electron.ipcRenderer.invoke('validation:getCleanerData')) as {
success: boolean
orderNumbers?: string[]
materialCodes?: string[]
}
if (result.success) { if (result.success) {
return { return {
orderNumbers: result.orderNumbers || [], orderNumbers: result.orderNumbers || [],

View File

@@ -15,6 +15,8 @@
"noUnusedParameters": false, "noUnusedParameters": false,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"esModuleInterop": true "esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
} }
} }