/** * Determines whether a value is a File object with enhanced type safety. * * Provides reliable File detection with environment compatibility, identifying * File objects used in web applications for file handling. File objects extend * Blob functionality with additional metadata like name and last modified time. * Returns false in environments where File API is not available. * * @param value - Value to test for File type * @returns Type-safe boolean indicating whether the value is a File object * * @example * Basic File detection: * ```typescript * import { isFile } from '@winglet/common-utils'; * * // True cases - File instances (in browser environment) * if (typeof File !== 'undefined') { * const file1 = new File(['content'], 'test.txt', { type: 'text/plain' }); * const file2 = new File(['

Hello

'], 'test.html', { type: 'text/html' }); * * console.log(isFile(file1)); // true * console.log(isFile(file2)); // true * } * * // False cases - not File objects * console.log(isFile(new Blob(['content']))); // false (Blob, not File) * console.log(isFile('filename.txt')); // false (string) * console.log(isFile({ name: 'file.txt', size: 100 })); // false (file-like object) * console.log(isFile(null)); // false * console.log(isFile(undefined)); // false * * // Environment without File API support (e.g., older Node.js) * console.log(isFile(anything)); // always false * ``` * * @example * File upload handling: * ```typescript * interface FileUploadHandler { * handleFileUpload(files: unknown[]): Promise; * } * * interface UploadResult { * success: boolean; * filename?: string; * size?: number; * error?: string; * } * * class WebFileUploadHandler implements FileUploadHandler { * async handleFileUpload(files: unknown[]): Promise { * const results: UploadResult[] = []; * * for (const file of files) { * if (!isFile(file)) { * results.push({ * success: false, * error: 'Invalid file object' * }); * continue; * } * * // TypeScript knows file is File * try { * const result = await this.processFile(file); * results.push({ * success: true, * filename: file.name, * size: file.size * }); * } catch (error) { * results.push({ * success: false, * filename: file.name, * error: error.message * }); * } * } * * return results; * } * * private async processFile(file: File): Promise { * // Validate file * if (file.size > 10 * 1024 * 1024) { // 10MB limit * throw new Error('File too large'); * } * * const allowedTypes = ['image/jpeg', 'image/png', 'text/plain', 'application/pdf']; * if (!allowedTypes.includes(file.type)) { * throw new Error('File type not allowed'); * } * * // Process file content * const content = await file.text(); * console.log(`Processing ${file.name}: ${content.length} characters`); * } * } * ``` * * @example * File input validation: * ```typescript * interface FileValidationRule { * name: string; * validate: (file: File) => boolean; * errorMessage: string; * } * * class FileValidator { * private rules: FileValidationRule[] = [ * { * name: 'size', * validate: (file) => file.size <= 5 * 1024 * 1024, // 5MB * errorMessage: 'File size must be less than 5MB' * }, * { * name: 'type', * validate: (file) => file.type.startsWith('image/'), * errorMessage: 'Only image files are allowed' * }, * { * name: 'name', * validate: (file) => file.name.length > 0 && file.name.length <= 255, * errorMessage: 'Filename must be between 1 and 255 characters' * } * ]; * * validateFile(file: unknown): { valid: boolean; errors: string[] } { * if (!isFile(file)) { * return { * valid: false, * errors: ['Invalid file object'] * }; * } * * const errors: string[] = []; * * for (const rule of this.rules) { * if (!rule.validate(file)) { * errors.push(rule.errorMessage); * } * } * * return { * valid: errors.length === 0, * errors * }; * } * * validateFiles(files: unknown[]): { * validFiles: File[]; * invalidFiles: Array<{ file: unknown; errors: string[] }> * } { * const validFiles: File[] = []; * const invalidFiles: Array<{ file: unknown; errors: string[] }> = []; * * for (const file of files) { * const validation = this.validateFile(file); * * if (validation.valid && isFile(file)) { * validFiles.push(file); * } else { * invalidFiles.push({ file, errors: validation.errors }); * } * } * * return { validFiles, invalidFiles }; * } * } * ``` * * @example * File processing with metadata: * ```typescript * interface FileMetadata { * name: string; * size: number; * type: string; * lastModified: number; * lastModifiedDate: Date; * } * * function extractFileMetadata(file: unknown): FileMetadata | null { * if (!isFile(file)) { * return null; * } * * // TypeScript knows file is File with all File properties * return { * name: file.name, * size: file.size, * type: file.type, * lastModified: file.lastModified, * lastModifiedDate: new Date(file.lastModified) * }; * } * * async function processFileWithMetadata(file: unknown) { * const metadata = extractFileMetadata(file); * * if (!metadata || !isFile(file)) { * throw new Error('Invalid file input'); * } * * console.log('File metadata:', metadata); * * // Read file content * const content = await file.text(); * * return { * metadata, * content, * processed: true, * processedAt: new Date() * }; * } * ``` * * @example * Drag and drop file handling: * ```typescript * class FileDropHandler { * setupDropZone(element: HTMLElement) { * element.addEventListener('dragover', this.handleDragOver); * element.addEventListener('drop', this.handleDrop); * } * * private handleDragOver = (event: DragEvent) => { * event.preventDefault(); * event.stopPropagation(); * }; * * private handleDrop = async (event: DragEvent) => { * event.preventDefault(); * event.stopPropagation(); * * const files = Array.from(event.dataTransfer?.files || []); * const validFiles = files.filter(isFile); * * if (validFiles.length !== files.length) { * console.warn(`${files.length - validFiles.length} invalid file objects detected`); * } * * for (const file of validFiles) { * await this.processDroppedFile(file); * } * }; * * private async processDroppedFile(file: File) { * console.log(`Processing dropped file: ${file.name} (${file.size} bytes)`); * * // Handle different file types * if (file.type.startsWith('image/')) { * await this.processImage(file); * } else if (file.type === 'text/plain') { * await this.processText(file); * } else { * console.log(`Unsupported file type: ${file.type}`); * } * } * * private async processImage(file: File) { * const imageUrl = URL.createObjectURL(file); * // Process image... * URL.revokeObjectURL(imageUrl); // Clean up * } * * private async processText(file: File) { * const text = await file.text(); * console.log(`Text content: ${text.substring(0, 100)}...`); * } * } * ``` * * @example * File API feature detection: * ```typescript * interface FileApiSupport { * file: boolean; * blob: boolean; * fileReader: boolean; * formData: boolean; * url: boolean; * } * * function detectFileApiSupport(): FileApiSupport { * return { * file: typeof File !== 'undefined', * blob: typeof Blob !== 'undefined', * fileReader: typeof FileReader !== 'undefined', * formData: typeof FormData !== 'undefined', * url: typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function' * }; * } * * function createFileIfSupported(content: string, filename: string): File | null { * const support = detectFileApiSupport(); * * if (!support.file) { * console.warn('File API not supported'); * return null; * } * * const file = new File([content], filename, { type: 'text/plain' }); * * if (isFile(file)) { * return file; * } * * console.error('Failed to create File object'); * return null; * } * ``` * * @remarks * **File vs Blob Relationship:** * - File extends Blob with additional properties (name, lastModified) * - All File objects are also Blob objects * - This function specifically checks for File, not just Blob * - Use `isBlob()` if you want to accept both File and Blob objects * * **Environment Compatibility:** * - File API is available in modern browsers * - Not available in older browsers or some server environments * - Node.js has limited File API support (may require polyfills) * - Always check environment support before using * * **File Properties Available:** * - `name` - filename string * - `size` - file size in bytes * - `type` - MIME type string * - `lastModified` - timestamp of last modification * - All Blob methods: `text()`, `arrayBuffer()`, `stream()`, `slice()` * * **Use Cases:** * - File upload handling * - Drag and drop file processing * - File input validation * - File metadata extraction * - Client-side file processing * * **Performance:** File and Blob instanceof checks with environment validation. * * **Related Functions:** * - Use `isBlob()` for Blob detection (includes File objects) * - Use `isArrayBuffer()` for binary buffer detection * - Use File API methods for file content access */ export declare const isFile: (value: unknown) => value is File;