TypeScript 에러: "'string | undefined' 형식은 'string' 형식에 할당할 수 없습니다" 🚨

제이슨·2024년 5월 18일
1

TypeScript를 사용하다가 마주친 에러에 대해 이야기해보려고 한다.

"'string | undefined' 형식은 'string' 형식에 할당할 수 없습니다"라는 에러인데, 이 에러가 발생하는 이유와 해결 방법에 대해 알아보자.

🤔 에러의 원인

NestJS에서 ConfigService를 사용해 환경 변수 값을 가져오는 코드를 작성하다가 이 에러를 만났다. 코드를 보면서 에러의 원인을 파악해 보자

constructor(private readonly configService: ConfigService) {
  this.REST_API_KEY = this.configService.get('REST_API_KEY');
  this.REDIRECT_URI = this.configService.get('REDIRECT_URI');
}

여기서 configService.get() 메서드는 string 또는 undefined를 반환하는데, this.REST_API_KEYthis.REDIRECT_URIstring 타입으로 선언되어 있다.

TypeScript의 strictNullChecks 옵션이 활성화된 경우, undefinedstring 타입에 할당할 수 없다고 판단하기 때문에 에러가 발생한다.

💡 해결 방법

이 에러를 해결하는 방법은 네 가지 정도 있다.

1. 타입 단언(Type Assertion) 사용하기

this.REST_API_KEY = this.configService.get('REST_API_KEY') as string;
this.REDIRECT_URI = this.configService.get('REDIRECT_URI') as string;

as string을 사용하면 TypeScript 컴파일러에게 해당 값이 확실히 string이라고 알려줄 수 있다.

하지만!! 런타임에서 undefined 값이 할당될 수 있으므로 주의해야 한다.

2. 기본값(Default Value) 제공하기

this.REST_API_KEY = this.configService.get('REST_API_KEY') || '';
this.REDIRECT_URI = this.configService.get('REDIRECT_URI') || '';

|| 연산자를 사용해서 configService.get() 메서드가 undefined를 반환하면 빈 문자열('')을 할당하도록 할 수 있다.

이렇게 하면 this.REST_API_KEYthis.REDIRECT_URI는 항상 string 타입의 값을 가지게 된다.

하지만, 역시 의도하는 기능(유효한 config 값을 불러오는 역할)이 수행됐는지 확인되는 시점은 해당 값을 사용하는 메서드가 호출될 때라는 점에서 매력적인 선택지가 아니다.

3. 널 병합 연산자(Nullish Coalescing Operator) 사용하기

this.REST_API_KEY = this.configService.get('REST_API_KEY') ?? '';
this.REDIRECT_URI = this.configService.get('REDIRECT_URI') ?? '';

configService.get() 메서드가 null 또는 undefined를 반환하면 ?? 연산자로 빈 문자열('')을 할당하도록 할 수 있다.

하지만, 이 방식 역시 1, 2번 방식과 마찬가지의 문제점을 가지고 있다.

4. 환경 변수의 존재 확인하고 error throw 하기

constructor(private readonly configService: ConfigService) {
  const restApiKey = this.configService.get('REST_API_KEY');
  const redirectUri = this.configService.get('REDIRECT_URI');

  if (!restApiKey || !redirectUri) {
    throw new Error('REST_API_KEY or REDIRECT_URI is missing');
  }

  this.REST_API_KEY = restApiKey;
  this.REDIRECT_URI = redirectUri;
}

configService.get() 메서드의 반환값을 먼저 변수에 할당한 뒤, 해당 값들이 존재하는지 확인한다. 만약 누락된 값이 있다면 에러를 던진다.

이렇게 하면 런타임에서 환경 변수의 존재를 보장할 수 있게 된다.

가장 맘에 드는 방식이다. 유효한 config 값을 불러오는 역할이 수행됐는지 확인되는 시점이 해당 값을 사용하는 메서드가 호출될 때가 아닌 서버가 구동되는 시점(정확히는 NestJS IoC 컨테이너에서 Provider 인스턴스를 생성하는 시점)이기 때문이다!!!

이 방식을 채택했고, 1차로 문제를 해결해 작성한 코드는 아래와 같다.

getConfigValue 메서드를 ConfigService 클래스로 이동한다면, 더 재사용성이 높은 코드가 완성될 것이다 🤩

...

@Injectable()
export class UsersService {
  private readonly restApiKey: string;
  private readonly redirectUri: string;

  constructor(private readonly configService: ConfigService) {
    this.restApiKey = this.getConfigValue('REST_API_KEY');
    this.redirectUri = this.getConfigValue('REDIRECT_URI');
  }

  private getConfigValue(key: string): string {
    const value = this.configService.get(key);
    if (!value) throw new Error(`${key} is missing`);
    return value;
  }
  
  ...
}
profile
계속 읽고 싶은 글을 쓰고 싶어요 ☺

0개의 댓글