mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Control flow analysis for element access with variable index (#57847)
This commit is contained in:
parent
316f1805f3
commit
a22aaf0ee7
@ -26622,13 +26622,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return getFlowCacheKey((node as NonNullExpression | ParenthesizedExpression).expression, declaredType, initialType, flowContainer);
|
||||
case SyntaxKind.QualifiedName:
|
||||
const left = getFlowCacheKey((node as QualifiedName).left, declaredType, initialType, flowContainer);
|
||||
return left && left + "." + (node as QualifiedName).right.escapedText;
|
||||
return left && `${left}.${(node as QualifiedName).right.escapedText}`;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
const propName = getAccessedPropertyName(node as AccessExpression);
|
||||
if (propName !== undefined) {
|
||||
const key = getFlowCacheKey((node as AccessExpression).expression, declaredType, initialType, flowContainer);
|
||||
return key && key + "." + propName;
|
||||
return key && `${key}.${propName}`;
|
||||
}
|
||||
if (isElementAccessExpression(node) && isIdentifier(node.argumentExpression)) {
|
||||
const symbol = getResolvedSymbol(node.argumentExpression);
|
||||
if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) {
|
||||
const key = getFlowCacheKey((node as AccessExpression).expression, declaredType, initialType, flowContainer);
|
||||
return key && `${key}.@${getSymbolId(symbol)}`;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
@ -26674,9 +26681,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
const sourcePropertyName = getAccessedPropertyName(source as AccessExpression);
|
||||
const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined;
|
||||
return sourcePropertyName !== undefined && targetPropertyName !== undefined && targetPropertyName === sourcePropertyName &&
|
||||
isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression);
|
||||
if (sourcePropertyName !== undefined) {
|
||||
const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined;
|
||||
if (targetPropertyName !== undefined) {
|
||||
return targetPropertyName === sourcePropertyName && isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression);
|
||||
}
|
||||
}
|
||||
if (isElementAccessExpression(source) && isElementAccessExpression(target) && isIdentifier(source.argumentExpression) && isIdentifier(target.argumentExpression)) {
|
||||
const symbol = getResolvedSymbol(source.argumentExpression);
|
||||
if (symbol === getResolvedSymbol(target.argumentExpression) && (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol))) {
|
||||
return isMatchingReference(source.expression, target.expression);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.QualifiedName:
|
||||
return isAccessExpression(target) &&
|
||||
(source as QualifiedName).right.escapedText === getAccessedPropertyName(target) &&
|
||||
|
||||
@ -0,0 +1,134 @@
|
||||
//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts] ////
|
||||
|
||||
=== controlFlowComputedPropertyNames.ts ===
|
||||
function f1(obj: Record<string, unknown>, key: string) {
|
||||
>f1 : Symbol(f1, Decl(controlFlowComputedPropertyNames.ts, 0, 0))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41))
|
||||
|
||||
if (typeof obj[key] === "string") {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41))
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
function f2(obj: Record<string, string | undefined>, key: string) {
|
||||
>f2 : Symbol(f2, Decl(controlFlowComputedPropertyNames.ts, 4, 1))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
|
||||
if (obj[key] !== undefined) {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
let key2 = key + key;
|
||||
>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
|
||||
if (obj[key2] !== undefined) {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
obj[key2].toUpperCase();
|
||||
>obj[key2].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
const key3 = key + key;
|
||||
>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52))
|
||||
|
||||
if (obj[key3] !== undefined) {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
obj[key3].toUpperCase();
|
||||
>obj[key3].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12))
|
||||
>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
type Thing = { a?: string, b?: number, c?: number };
|
||||
>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1))
|
||||
>a : Symbol(a, Decl(controlFlowComputedPropertyNames.ts, 20, 14))
|
||||
>b : Symbol(b, Decl(controlFlowComputedPropertyNames.ts, 20, 26))
|
||||
>c : Symbol(c, Decl(controlFlowComputedPropertyNames.ts, 20, 38))
|
||||
|
||||
function f3(obj: Thing, key: keyof Thing) {
|
||||
>f3 : Symbol(f3, Decl(controlFlowComputedPropertyNames.ts, 20, 52))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1))
|
||||
|
||||
if (obj[key] !== undefined) {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
if (typeof obj[key] === "string") {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
if (typeof obj[key] === "number") {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
|
||||
obj[key].toFixed();
|
||||
>obj[key].toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23))
|
||||
>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f4<K extends string>(obj: Record<K, string | undefined>, key: K) {
|
||||
>f4 : Symbol(f4, Decl(controlFlowComputedPropertyNames.ts, 31, 1))
|
||||
>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65))
|
||||
>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12))
|
||||
|
||||
if (obj[key]) {
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65))
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30))
|
||||
>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
163
tests/baselines/reference/controlFlowComputedPropertyNames.types
Normal file
163
tests/baselines/reference/controlFlowComputedPropertyNames.types
Normal file
@ -0,0 +1,163 @@
|
||||
//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts] ////
|
||||
|
||||
=== controlFlowComputedPropertyNames.ts ===
|
||||
function f1(obj: Record<string, unknown>, key: string) {
|
||||
>f1 : (obj: Record<string, unknown>, key: string) => void
|
||||
>obj : Record<string, unknown>
|
||||
>key : string
|
||||
|
||||
if (typeof obj[key] === "string") {
|
||||
>typeof obj[key] === "string" : boolean
|
||||
>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>obj[key] : unknown
|
||||
>obj : Record<string, unknown>
|
||||
>key : string
|
||||
>"string" : "string"
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase() : string
|
||||
>obj[key].toUpperCase : () => string
|
||||
>obj[key] : string
|
||||
>obj : Record<string, unknown>
|
||||
>key : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
}
|
||||
|
||||
function f2(obj: Record<string, string | undefined>, key: string) {
|
||||
>f2 : (obj: Record<string, string | undefined>, key: string) => void
|
||||
>obj : Record<string, string | undefined>
|
||||
>key : string
|
||||
|
||||
if (obj[key] !== undefined) {
|
||||
>obj[key] !== undefined : boolean
|
||||
>obj[key] : string | undefined
|
||||
>obj : Record<string, string | undefined>
|
||||
>key : string
|
||||
>undefined : undefined
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase() : string
|
||||
>obj[key].toUpperCase : () => string
|
||||
>obj[key] : string
|
||||
>obj : Record<string, string | undefined>
|
||||
>key : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
let key2 = key + key;
|
||||
>key2 : string
|
||||
>key + key : string
|
||||
>key : string
|
||||
>key : string
|
||||
|
||||
if (obj[key2] !== undefined) {
|
||||
>obj[key2] !== undefined : boolean
|
||||
>obj[key2] : string | undefined
|
||||
>obj : Record<string, string | undefined>
|
||||
>key2 : string
|
||||
>undefined : undefined
|
||||
|
||||
obj[key2].toUpperCase();
|
||||
>obj[key2].toUpperCase() : string
|
||||
>obj[key2].toUpperCase : () => string
|
||||
>obj[key2] : string
|
||||
>obj : Record<string, string | undefined>
|
||||
>key2 : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
const key3 = key + key;
|
||||
>key3 : string
|
||||
>key + key : string
|
||||
>key : string
|
||||
>key : string
|
||||
|
||||
if (obj[key3] !== undefined) {
|
||||
>obj[key3] !== undefined : boolean
|
||||
>obj[key3] : string | undefined
|
||||
>obj : Record<string, string | undefined>
|
||||
>key3 : string
|
||||
>undefined : undefined
|
||||
|
||||
obj[key3].toUpperCase();
|
||||
>obj[key3].toUpperCase() : string
|
||||
>obj[key3].toUpperCase : () => string
|
||||
>obj[key3] : string
|
||||
>obj : Record<string, string | undefined>
|
||||
>key3 : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
}
|
||||
|
||||
type Thing = { a?: string, b?: number, c?: number };
|
||||
>Thing : { a?: string | undefined; b?: number | undefined; c?: number | undefined; }
|
||||
>a : string | undefined
|
||||
>b : number | undefined
|
||||
>c : number | undefined
|
||||
|
||||
function f3(obj: Thing, key: keyof Thing) {
|
||||
>f3 : (obj: Thing, key: keyof Thing) => void
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
|
||||
if (obj[key] !== undefined) {
|
||||
>obj[key] !== undefined : boolean
|
||||
>obj[key] : string | number | undefined
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
>undefined : undefined
|
||||
|
||||
if (typeof obj[key] === "string") {
|
||||
>typeof obj[key] === "string" : boolean
|
||||
>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>obj[key] : string | number
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
>"string" : "string"
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase() : string
|
||||
>obj[key].toUpperCase : () => string
|
||||
>obj[key] : string
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
if (typeof obj[key] === "number") {
|
||||
>typeof obj[key] === "number" : boolean
|
||||
>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>obj[key] : string | number
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
>"number" : "number"
|
||||
|
||||
obj[key].toFixed();
|
||||
>obj[key].toFixed() : string
|
||||
>obj[key].toFixed : (fractionDigits?: number | undefined) => string
|
||||
>obj[key] : number
|
||||
>obj : Thing
|
||||
>key : keyof Thing
|
||||
>toFixed : (fractionDigits?: number | undefined) => string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f4<K extends string>(obj: Record<K, string | undefined>, key: K) {
|
||||
>f4 : <K extends string>(obj: Record<K, string | undefined>, key: K) => void
|
||||
>obj : Record<K, string | undefined>
|
||||
>key : K
|
||||
|
||||
if (obj[key]) {
|
||||
>obj[key] : Record<K, string | undefined>[K]
|
||||
>obj : Record<K, string | undefined>
|
||||
>key : K
|
||||
|
||||
obj[key].toUpperCase();
|
||||
>obj[key].toUpperCase() : string
|
||||
>obj[key].toUpperCase : () => string
|
||||
>obj[key] : string
|
||||
>obj : Record<K, string | undefined>
|
||||
>key : K
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,14 +60,14 @@ class Test {
|
||||
>[] : never[]
|
||||
}
|
||||
this.entries[name]?.push(entry);
|
||||
>this.entries[name]?.push(entry) : number | undefined
|
||||
>this.entries[name]?.push : ((...items: Types[T][]) => number) | undefined
|
||||
>this.entries[name] : Types[T][] | undefined
|
||||
>this.entries[name]?.push(entry) : number
|
||||
>this.entries[name]?.push : (...items: Types[T][]) => number
|
||||
>this.entries[name] : Types[T][]
|
||||
>this.entries : { first?: { a1: true; }[] | undefined; second?: { a2: true; }[] | undefined; third?: { a3: true; }[] | undefined; }
|
||||
>this : this
|
||||
>entries : { first?: { a1: true; }[] | undefined; second?: { a2: true; }[] | undefined; third?: { a3: true; }[] | undefined; }
|
||||
>name : T
|
||||
>push : ((...items: Types[T][]) => number) | undefined
|
||||
>push : (...items: Types[T][]) => number
|
||||
>entry : Types[T]
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +33,7 @@ noUncheckedIndexedAccess.ts(90,7): error TS2322: Type 'string | undefined' is no
|
||||
Type 'undefined' is not assignable to type 'string'.
|
||||
noUncheckedIndexedAccess.ts(98,5): error TS2322: Type 'undefined' is not assignable to type '{ [key: string]: string; a: string; b: string; }[Key]'.
|
||||
Type 'undefined' is not assignable to type 'string'.
|
||||
noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'string | undefined' is not assignable to type 'string'.
|
||||
Type 'undefined' is not assignable to type 'string'.
|
||||
noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'undefined' is not assignable to type 'string'.
|
||||
|
||||
|
||||
==== noUncheckedIndexedAccess.ts (31 errors) ====
|
||||
@ -203,8 +202,7 @@ noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'string | undefined' is n
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
|
||||
const v: string = myRecord2[key]; // Should error
|
||||
~
|
||||
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
|
||||
};
|
||||
|
||||
|
||||
@ -412,7 +412,7 @@ const fn3 = <Key extends keyof typeof myRecord2>(key: Key) => {
|
||||
|
||||
const v: string = myRecord2[key]; // Should error
|
||||
>v : string
|
||||
>myRecord2[key] : string | undefined
|
||||
>myRecord2[key] : undefined
|
||||
>myRecord2 : { [key: string]: string; a: string; b: string; }
|
||||
>key : Key
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
function f1(obj: Record<string, unknown>, key: string) {
|
||||
if (typeof obj[key] === "string") {
|
||||
obj[key].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
function f2(obj: Record<string, string | undefined>, key: string) {
|
||||
if (obj[key] !== undefined) {
|
||||
obj[key].toUpperCase();
|
||||
}
|
||||
let key2 = key + key;
|
||||
if (obj[key2] !== undefined) {
|
||||
obj[key2].toUpperCase();
|
||||
}
|
||||
const key3 = key + key;
|
||||
if (obj[key3] !== undefined) {
|
||||
obj[key3].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
type Thing = { a?: string, b?: number, c?: number };
|
||||
|
||||
function f3(obj: Thing, key: keyof Thing) {
|
||||
if (obj[key] !== undefined) {
|
||||
if (typeof obj[key] === "string") {
|
||||
obj[key].toUpperCase();
|
||||
}
|
||||
if (typeof obj[key] === "number") {
|
||||
obj[key].toFixed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f4<K extends string>(obj: Record<K, string | undefined>, key: K) {
|
||||
if (obj[key]) {
|
||||
obj[key].toUpperCase();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user