strict: true в tsconfig.json — это сокращение, включающее группу флагов сразу:
{
"compilerOptions": {
"strict": true
// Эквивалентно включению всех этих флагов:
// "strictNullChecks": true,
// "noImplicitAny": true,
// "strictFunctionTypes": true,
// "strictBindCallApply": true,
// "strictPropertyInitialization": true,
// "noImplicitThis": true,
// "alwaysStrict": true,
// "useUnknownInCatchVariables": true
}
}Без него null и undefined совместимы с любым типом:
// Без strictNullChecks:
let name: string = null // OK! (но это баг)
let age: number = undefined // OK! (но это баг)
// С strictNullChecks:
let name: string = null // Ошибка TS!
let safe: string | null = null // OK — явно указали null
// Теперь нужно проверять перед использованием:
function getLength(s: string | null): number {
if (s === null) return 0
return s.length // TypeScript знает: s — string
}
// Оператор ?. и ?? — стали нужны:
const length = user?.name?.length ?? 0// Без noImplicitAny:
function process(data) { // data: any — молча
return data.whatever // нет ошибки TypeScript
}
// С noImplicitAny:
function process(data) { // Ошибка TS: Parameter 'data' implicitly has 'any' type
return data
}
function process(data: unknown) { // OK — явный unknown
// нужна проверка типа
}
function process(data: string[]): string { // OK — явный тип
return data.join(', ')
}type Handler = (event: MouseEvent) => void
// Без strictFunctionTypes:
const handler: Handler = (e: Event) => {} // OK (бивариантно — опасно)
// С strictFunctionTypes:
// const handler: Handler = (e: Event) => {} // Ошибка TS!
// Event шире чем MouseEvent — небезопасно
const handler: Handler = (e: MouseEvent) => {} // OK — точный типЭтот флаг НЕ входит в strict, но очень полезен:
// tsconfig.json: "noUncheckedIndexedAccess": true
const arr = [1, 2, 3]
const first = arr[0] // тип: number | undefined (а не number!)
if (first !== undefined) {
first.toFixed(2) // OK — проверили
}
const obj: Record<string, number> = { a: 1 }
const val = obj['b'] // тип: number | undefinedclass User {
name: string // Ошибка TS: не инициализировано в конструкторе
age!: number // ! — говорим TS "доверяй мне, будет инициализировано"
constructor(name: string) {
this.name = name // OK
// age не инициализирован — баг!
}
}// Без флага:
try { /* ... */ } catch (e) {
e.message // e: any — нет ошибки, но небезопасно
}
// С useUnknownInCatchVariables (входит в strict):
try { /* ... */ } catch (e) {
// e: unknown — нужна проверка типа!
if (e instanceof Error) {
console.log(e.message) // OK
}
}1. Выявляет скрытые баги на этапе компиляции
2. Заставляет явно описывать типы — документирует намерения
3. Делает рефакторинг безопаснее
4. Требуется для современных проектов — NestJS, Angular и другие предполагают strict
Защитное программирование: пишем JavaScript-код который соответствует принципам strict TypeScript — явные проверки null, типов, индексов
// TypeScript strict mode запрещает неявные any, null без проверки,
// доступ без проверки и т.д. Покажем как писать "строгий" JS.
// --- strictNullChecks аналог: всегда проверяй null/undefined ---
function getLength(s) {
// Strict TypeScript потребовал бы: if (s === null) return 0
if (s == null) return 0
return s.length
}
function getUser(id) {
const users = new Map([
[1, { id: 1, name: 'Алексей' }],
[2, { id: 2, name: 'Ольга' }],
])
return users.get(id) ?? null // всегда явно, не undefined
}
// --- noImplicitAny аналог: явные JSDoc типы или проверки ---
/**
* @param {string[]} items
* @returns {string}
*/
function joinItems(items) {
if (!Array.isArray(items)) {
throw new TypeError(`joinItems: ожидается массив, получен ${typeof items}`)
}
return items.join(', ')
}
// --- noUncheckedIndexedAccess аналог: безопасный доступ по индексу ---
function safeGet(arr, index) {
if (index < 0 || index >= arr.length) return undefined
return arr[index]
}
function safeFirst(arr) {
return arr.length > 0 ? arr[0] : undefined
}
function safeLast(arr) {
return arr.length > 0 ? arr[arr.length - 1] : undefined
}
// --- useUnknownInCatchVariables аналог: безопасный catch ---
function safeJsonParse(text) {
try {
return { data: JSON.parse(text), error: null }
} catch (e) {
// В TypeScript с useUnknownInCatchVariables e: unknown
// Проверяем тип перед использованием
const message = e instanceof Error ? e.message : String(e)
return { data: null, error: message }
}
}
// --- strictFunctionTypes аналог: проверяй что передаёшь правильный callback ---
function processNumbers(arr, callback) {
if (typeof callback !== 'function') {
throw new TypeError('processNumbers: второй аргумент должен быть функцией')
}
return arr.map(item => {
if (typeof item !== 'number') {
throw new TypeError(`processNumbers: ожидается число, получен ${typeof item}`)
}
return callback(item)
})
}
// --- strictPropertyInitialization аналог: всегда инициализируй в конструкторе ---
class UserProfile {
// Каждое поле инициализировано — нет undefined в конструкторе
constructor(data) {
if (!data || typeof data !== 'object') {
throw new TypeError('UserProfile: data обязателен')
}
this.id = data.id ?? null
this.name = typeof data.name === 'string' ? data.name : ''
this.email = typeof data.email === 'string' ? data.email : ''
this.age = typeof data.age === 'number' ? data.age : 0
}
isValid() {
return this.id !== null && this.name.length > 0 && this.email.includes('@')
}
}
// --- Демонстрация ---
console.log('=== Проверки null (strictNullChecks) ===')
console.log(getLength(null)) // 0
console.log(getLength(undefined)) // 0
console.log(getLength('hello')) // 5
const user = getUser(1)
if (user !== null) {
console.log('Пользователь:', user.name)
} else {
console.log('Пользователь не найден')
}
console.log(getUser(99)) // null — явно, не undefined
console.log('\n=== Безопасный доступ по индексу (noUncheckedIndexedAccess) ===')
const arr = [10, 20, 30]
console.log(safeGet(arr, 1)) // 20
console.log(safeGet(arr, 10)) // undefined — не ошибка
console.log(safeFirst([])) // undefined
console.log(safeLast([1,2,3])) // 3
console.log('\n=== Безопасный catch (useUnknownInCatchVariables) ===')
const r1 = safeJsonParse('{"name":"Алексей"}')
console.log(r1.data) // { name: 'Алексей' }
console.log(r1.error) // null
const r2 = safeJsonParse('{invalid json}')
console.log(r2.data) // null
console.log(r2.error) // сообщение об ошибке
console.log('\n=== Инициализация в конструкторе (strictPropertyInitialization) ===')
const profile = new UserProfile({ id: 1, name: 'Алексей', email: 'a@b.ru', age: 30 })
console.log(profile.isValid()) // true
const empty = new UserProfile({})
console.log(empty.name) // '' — не undefined
console.log(empty.isValid()) // falsestrict: true в tsconfig.json — это сокращение, включающее группу флагов сразу:
{
"compilerOptions": {
"strict": true
// Эквивалентно включению всех этих флагов:
// "strictNullChecks": true,
// "noImplicitAny": true,
// "strictFunctionTypes": true,
// "strictBindCallApply": true,
// "strictPropertyInitialization": true,
// "noImplicitThis": true,
// "alwaysStrict": true,
// "useUnknownInCatchVariables": true
}
}Без него null и undefined совместимы с любым типом:
// Без strictNullChecks:
let name: string = null // OK! (но это баг)
let age: number = undefined // OK! (но это баг)
// С strictNullChecks:
let name: string = null // Ошибка TS!
let safe: string | null = null // OK — явно указали null
// Теперь нужно проверять перед использованием:
function getLength(s: string | null): number {
if (s === null) return 0
return s.length // TypeScript знает: s — string
}
// Оператор ?. и ?? — стали нужны:
const length = user?.name?.length ?? 0// Без noImplicitAny:
function process(data) { // data: any — молча
return data.whatever // нет ошибки TypeScript
}
// С noImplicitAny:
function process(data) { // Ошибка TS: Parameter 'data' implicitly has 'any' type
return data
}
function process(data: unknown) { // OK — явный unknown
// нужна проверка типа
}
function process(data: string[]): string { // OK — явный тип
return data.join(', ')
}type Handler = (event: MouseEvent) => void
// Без strictFunctionTypes:
const handler: Handler = (e: Event) => {} // OK (бивариантно — опасно)
// С strictFunctionTypes:
// const handler: Handler = (e: Event) => {} // Ошибка TS!
// Event шире чем MouseEvent — небезопасно
const handler: Handler = (e: MouseEvent) => {} // OK — точный типЭтот флаг НЕ входит в strict, но очень полезен:
// tsconfig.json: "noUncheckedIndexedAccess": true
const arr = [1, 2, 3]
const first = arr[0] // тип: number | undefined (а не number!)
if (first !== undefined) {
first.toFixed(2) // OK — проверили
}
const obj: Record<string, number> = { a: 1 }
const val = obj['b'] // тип: number | undefinedclass User {
name: string // Ошибка TS: не инициализировано в конструкторе
age!: number // ! — говорим TS "доверяй мне, будет инициализировано"
constructor(name: string) {
this.name = name // OK
// age не инициализирован — баг!
}
}// Без флага:
try { /* ... */ } catch (e) {
e.message // e: any — нет ошибки, но небезопасно
}
// С useUnknownInCatchVariables (входит в strict):
try { /* ... */ } catch (e) {
// e: unknown — нужна проверка типа!
if (e instanceof Error) {
console.log(e.message) // OK
}
}1. Выявляет скрытые баги на этапе компиляции
2. Заставляет явно описывать типы — документирует намерения
3. Делает рефакторинг безопаснее
4. Требуется для современных проектов — NestJS, Angular и другие предполагают strict
Защитное программирование: пишем JavaScript-код который соответствует принципам strict TypeScript — явные проверки null, типов, индексов
// TypeScript strict mode запрещает неявные any, null без проверки,
// доступ без проверки и т.д. Покажем как писать "строгий" JS.
// --- strictNullChecks аналог: всегда проверяй null/undefined ---
function getLength(s) {
// Strict TypeScript потребовал бы: if (s === null) return 0
if (s == null) return 0
return s.length
}
function getUser(id) {
const users = new Map([
[1, { id: 1, name: 'Алексей' }],
[2, { id: 2, name: 'Ольга' }],
])
return users.get(id) ?? null // всегда явно, не undefined
}
// --- noImplicitAny аналог: явные JSDoc типы или проверки ---
/**
* @param {string[]} items
* @returns {string}
*/
function joinItems(items) {
if (!Array.isArray(items)) {
throw new TypeError(`joinItems: ожидается массив, получен ${typeof items}`)
}
return items.join(', ')
}
// --- noUncheckedIndexedAccess аналог: безопасный доступ по индексу ---
function safeGet(arr, index) {
if (index < 0 || index >= arr.length) return undefined
return arr[index]
}
function safeFirst(arr) {
return arr.length > 0 ? arr[0] : undefined
}
function safeLast(arr) {
return arr.length > 0 ? arr[arr.length - 1] : undefined
}
// --- useUnknownInCatchVariables аналог: безопасный catch ---
function safeJsonParse(text) {
try {
return { data: JSON.parse(text), error: null }
} catch (e) {
// В TypeScript с useUnknownInCatchVariables e: unknown
// Проверяем тип перед использованием
const message = e instanceof Error ? e.message : String(e)
return { data: null, error: message }
}
}
// --- strictFunctionTypes аналог: проверяй что передаёшь правильный callback ---
function processNumbers(arr, callback) {
if (typeof callback !== 'function') {
throw new TypeError('processNumbers: второй аргумент должен быть функцией')
}
return arr.map(item => {
if (typeof item !== 'number') {
throw new TypeError(`processNumbers: ожидается число, получен ${typeof item}`)
}
return callback(item)
})
}
// --- strictPropertyInitialization аналог: всегда инициализируй в конструкторе ---
class UserProfile {
// Каждое поле инициализировано — нет undefined в конструкторе
constructor(data) {
if (!data || typeof data !== 'object') {
throw new TypeError('UserProfile: data обязателен')
}
this.id = data.id ?? null
this.name = typeof data.name === 'string' ? data.name : ''
this.email = typeof data.email === 'string' ? data.email : ''
this.age = typeof data.age === 'number' ? data.age : 0
}
isValid() {
return this.id !== null && this.name.length > 0 && this.email.includes('@')
}
}
// --- Демонстрация ---
console.log('=== Проверки null (strictNullChecks) ===')
console.log(getLength(null)) // 0
console.log(getLength(undefined)) // 0
console.log(getLength('hello')) // 5
const user = getUser(1)
if (user !== null) {
console.log('Пользователь:', user.name)
} else {
console.log('Пользователь не найден')
}
console.log(getUser(99)) // null — явно, не undefined
console.log('\n=== Безопасный доступ по индексу (noUncheckedIndexedAccess) ===')
const arr = [10, 20, 30]
console.log(safeGet(arr, 1)) // 20
console.log(safeGet(arr, 10)) // undefined — не ошибка
console.log(safeFirst([])) // undefined
console.log(safeLast([1,2,3])) // 3
console.log('\n=== Безопасный catch (useUnknownInCatchVariables) ===')
const r1 = safeJsonParse('{"name":"Алексей"}')
console.log(r1.data) // { name: 'Алексей' }
console.log(r1.error) // null
const r2 = safeJsonParse('{invalid json}')
console.log(r2.data) // null
console.log(r2.error) // сообщение об ошибке
console.log('\n=== Инициализация в конструкторе (strictPropertyInitialization) ===')
const profile = new UserProfile({ id: 1, name: 'Алексей', email: 'a@b.ru', age: 30 })
console.log(profile.isValid()) // true
const empty = new UserProfile({})
console.log(empty.name) // '' — не undefined
console.log(empty.isValid()) // falseРеализуй функцию `strictGet(obj, key)` — возвращает значение obj[key] если key существует в obj, иначе бросает TypeError с сообщением "Ключ \"key\" не найден". Реализуй функцию `strictFirst(arr)` — возвращает первый элемент массива, бросает RangeError "Массив пуст" если массив пустой. Реализуй функцию `parseNumber(value)` — пробует преобразовать value в число: если value уже число — возвращает его, если строка — парсит через parseFloat, если результат NaN или value другого типа — бросает TypeError.
strictGet: if (Object.prototype.hasOwnProperty.call(obj, key)) return obj[key]; throw new TypeError(...). strictFirst: if (arr.length === 0) throw new RangeError("Массив пуст"); return arr[0]. parseNumber: if (typeof value === "number") { if (isNaN(value)) throw new TypeError(...); return value } if (typeof value === "string") { const n = parseFloat(value); if (isNaN(n)) throw new TypeError(...); return n } throw new TypeError(...).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке