Jieunny์˜ ๋ธ”๋กœ๊ทธ

[TS] ๋ฐ˜๋ณต๊ธฐ์™€ ์ƒ์„ฑ๊ธฐ ๋ณธ๋ฌธ

Study/TypeScript

[TS] ๋ฐ˜๋ณต๊ธฐ์™€ ์ƒ์„ฑ๊ธฐ

Jieunny 2023. 2. 3. 11:48

๐Ÿ“ฃ  ๋ฐ˜๋ณต๊ธฐ ์ดํ•ดํ•˜๊ธฐ

โœ”๏ธ tsconfig.json์—์„œ downlevelIteration ํ•ญ๋ชฉ์„ true๋กœ ์„ค์ •ํ•ด์•ผ ๋ฐ˜๋ณต๊ธฐ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

 

๐Ÿ“ ๋ฐ˜๋ณต๊ธฐ์™€ ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž

โžฐ for...of ๊ตฌ๋ฌธ์€ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ๋„ '๋ฐ˜๋ณต๊ธฐ'๋ผ๋Š” ์ฃผ์ œ๋กœ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค.

โžฐ ๋ฐ˜๋ณต๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์ด ์žˆ๋Š” ๊ฐ์ฒด์ด๋‹ค.

    ๏น’ next๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. 

    ๏น’ next ๋ฉ”์„œ๋“œ๋Š” value์™€ done์ด๋ผ๋Š” ๋‘ ๊ฐœ์˜ ์†์„ฑ์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

// ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž
export const createRangeIterable = (from: number, to: number) => {
    let currentValue = from
    return {
        next() {
            const value = currentValue < to ? currentValue++ : undefined
            const dome = value == undefined
            return {value, done}
        }
    }
}

โžฐ createRangeIterable ํ•จ์ˆ˜๋Š” next ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋ฐ˜๋ณต๊ธฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

โžฐ ๋ฐ˜๋ณต๊ธฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ '๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž(iterable)' ๋ผ๊ณ  ํ•œ๋‹ค.

 

import {createRangeIterable} fromo './createRangeIterable'
const iterator = createRangeIterable(1, 3+1)	// ๋ฐ˜๋ณต๊ธฐ๋Š” ํ˜„์žฌ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
while(true) {
    const {value, done} = iterator.next()	// ๋ฐ˜๋ณต๊ธฐ๋ฅผ ๋™์ž‘์‹œํ‚ค๋‹ค.
    if(done) break
    console.log(value)	// 1 2 3
}

โžฐ createRangeIterable ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ฐ˜๋ณต๊ธฐ๋ฅผ iterator ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค.

โžฐ ๋ฐ˜๋ณต๊ธฐ๋Š” ์œ„์ฒ˜๋Ÿผ ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

โžฐ ์‚ฌ์šฉ์ž๊ฐ€ for...of ๊ตฌ๋ฌธ์„ ์ž‘์„ฑํ•˜๋ฉด TSC ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ด์ฒ˜๋Ÿผ ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž์™€ ๋ฐ˜๋ณต๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.

 

๐Ÿ“ ๋ฐ˜๋ณต๊ธฐ๋Š” ์™œ ํ•„์š”ํ•œ๊ฐ€?

โžฐ range ํ•จ์ˆ˜๋ž‘ ๋น„๊ตํ–ˆ์„ ๋•Œ, range ํ•จ์ˆ˜๋Š” ๊ฐ’์ด ํ•„์š”ํ•œ ์‹œ์ ๋ณด๋‹ค ์ด์ „์— ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•œ๋‹ค.

โžฐ ๋ฐ˜๋ณต๊ธฐ๋Š” ๊ฐ’์ด ํ•„์š”ํ•œ ์‹œ์ ์— ๋น„๋กœ์†Œ ์ƒ์„ฑํ•œ๋‹ค.

โžฐ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์‹œ์Šคํ…œ ๋ฉ”๋ชจ๋ฆฌ์˜ ํšจ์œจ์„ฑ ๊ด€์ ์—์„œ ๋ณด๋ฉด ๋ฐ˜๋ณต๊ธฐ๊ฐ€ ํ›จ์”ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ๊ฒŒ ์†Œ๋ชจํ•œ๋‹ค.

 

๐Ÿ“ for...of ๊ตฌ๋ฌธ๊ณผ [Symbol.iterator] ๋ฉ”์„œ๋“œ

โžฐ range ํ•จ์ˆ˜๋Š” for...of ๊ตฌ๋ฌธ์˜ of ๋’ค์— ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

import {range} from './range'
for(let value of range(1, 3+1))
	console.log(value)

 

โžฐ ํ•˜์ง€๋งŒ ๋ฐ˜๋ณต๊ธฐ๋ฅผ of ๋’ค์— ์ ์šฉํ•˜๋ฉด '[Symbol.iterator]() ๋ฉ”์„œ๋“œ๊ฐ€ ์—†๋‹ค' ๋ผ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

import {createRangeIterable} from './createRangeIterable'
const iterable = createRangeIterable(1, 3+1)
for(let value of iterable)
	// ์˜ค๋ฅ˜
    console.log(value)

โžฐ ์ด ์˜ค๋ฅ˜๋Š” createRangeIterable ํ•จ์ˆ˜๋ฅผ ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

โžฐ ํด๋ž˜์Šค๋Š” for...of ๊ตฌ๋ฌธ์˜ of ๋’ค์— ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 

export class RangeIterable {
    constructor(public from: number, public to: number) {}
    [Symbol.iterator]() {
        const that = this
        let currentValue = that.from
        return {
            next() {
                const value = currentValue < that.to ? currentValue++ : undefined
                const done = value == undefined
                return {value, done}
            }
        }
    }
}

โžฐ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋Š” function ํ‚ค์›Œ๋“œ๊ฐ€ ์ƒ๋žต๋˜์—ˆ์„ ๋ฟ function ํ‚ค์›Œ๋“œ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ํ•จ์ˆ˜์ด๋‹ค.

โžฐ function ํ‚ค์›Œ๋“œ๋กœ ๋งŒ๋“ค์–ด์ง„ ํ•จ์ˆ˜๋Š” ๋‚ด๋ถ€์—์„œ this ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โžฐ 4ํ–‰์˜ this๋Š” RangeIterable ํ•จ์ˆ˜๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š”๋ฐ, next ํ•จ์ˆ˜ ๋˜ํ•œ function ํ‚ค์›Œ๋“œ๊ฐ€ ์ƒ๋žต๋œ ๋ฉ”์„œ๋“œ์ด๋ฏ€๋กœ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ next์˜ this๋กœ ํ•ด์„ํ•˜์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ์ฝ”๋“œ ํŠธ๋ฆญ์ด๋‹ค.

 

๐Ÿ“ Iterable<T>์™€ Iterator<T> ์ธํ„ฐํŽ˜์ด์Šค

โžฐ ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž์— Iterable<T>์™€ Iterator<T> ์ œ๋„ค๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

1๏ธโƒฃ Iterable<T>

class ๊ตฌํ˜„ ํด๋ž˜์Šค implements Iterable<์ƒ์„ฑํ•  ๊ฐ’์˜ ํƒ€์ž…> {}

โžฐ ์ž์‹ ์„ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ [Symbol.iterator] ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ•  

 

2๏ธโƒฃ Iterator<T>

[Symbol.iterator](): Iterator<์ƒ์„ฑํ•  ๊ฐ’์˜ ํƒ€์ž…> {}

โžฐ ๋ฐ˜๋ณต๊ธฐ๊ฐ€ ์ƒ์„ฑํ•  ๊ฐ’์˜ ํƒ€์ž…์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์•Œ๋ ค์ค€๋‹ค.

 

export class StringIterable implements Iterable<string> {
    constructor(private strings: string[] = [], private currentIndex: number = 0) }{}
    [Symbol.iterator](): Iterator<string> {
        const that = this
        let currentIndex = that.currentIndex, length = that.strings.length
        
        const iterator: Iterator<string> = {
            next(): {value: string, done: boolean} {
                const value = currentValue < length ? that.strings[currentIndex++] : undefined
                const done = value == undefined
                return {value, done}
           	}
        }
        return iterator
    }
}

๐Ÿ“ฃ  ์ƒ์„ฑ๊ธฐ ์ดํ•ดํ•˜๊ธฐ

โœ”๏ธ ESNext JS์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” yield๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

โžฐ yield๋Š” return ํ‚ค์›Œ๋“œ์ฒ˜๋Ÿผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

โžฐ yield๋Š” ๋ฐ˜๋“œ์‹œ function* ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ํ•จ์ˆ˜์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

โžฐ function* ํ‚ค์›Œ๋“œ๋กœ ๋งŒ๋“  ํ•จ์ˆ˜๋ฅผ '์ƒ์„ฑ๊ธฐ(generator)'๋ผ๊ณ  ํ•œ๋‹ค.

โžฐ ์ƒ์„ฑ๊ธฐ๋Š” ๋ฐ˜๋ณต๊ธฐ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด ์ค€๋‹ค(๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž๋กœ ๋™์ž‘ํ•œ๋‹ค)

export function* generator() {
    console.log('generator started...')
    let value = 1
    while(value < 4)
        yield value++
    console.log('generator finished...')
}

for(let value of generator())
    console.log(value)
    
// generator started...
// 1
// 2
// 3
// generator finished...

 

๐Ÿ“ setInterval ํ•จ์ˆ˜์™€ ์ƒ์„ฑ๊ธฐ์˜ ์œ ์‚ฌ์„ฑ

โžฐ ์ƒ์„ฑ๊ธฐ๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹ : ์„ธ๋ฏธ์ฝ”๋ฃจํ‹ด

โž• ์„ธ๋ฏธ์ฝ”๋ฃจํ‹ด์ด๋ž€?

๋”๋ณด๊ธฐ

โžฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๊ฐ€ ๋งˆ์น˜ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ธฐ๋Šฅ

 

โžฐ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” setInterval ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์„ธ๋ฏธ์ฝ”๋ฃจํ‹ด ๋™์ž‘ ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

const period = 1000
let count = 0
console.log('program started...')
const id = setInterval(() => {
    if(count >= 3) {
        clearInterval(id)
        console.log('program finished...')
    }
    else
        console.log(++count)
},period)

// program started...
// 1 1์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ 1 2 3์ด ์ถœ๋ ฅ๋œ๋‹ค.
// 2
// 3
// program finished

 

๐Ÿ“ function* ํ‚ค์›Œ๋“œ

โžฐ ์ƒ์„ฑ๊ธฐ๋Š” ์˜ค์ง function* ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ๋Š” ์ƒ์„ฑ๊ธฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค.

โžฐ function* ์€ ๊ทธ ์ž์ฒด๋กœ ํ‚ค์›Œ๋“œ์ด๊ณ , function ํ‚ค์›Œ๋“œ์— *์„ ๋ถ™์ธ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.

 

๐Ÿ“ yield ํ‚ค์›Œ๋“œ

โžฐ ๋ฐ˜๋ณต๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋ฉฐ, ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž ์—ญํ• ๋„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

// ์ƒ์„ฑ๊ธฐ
export function* rangeGenerator(from: number, to: number) {
    let value = from
    while(value < to) {
        yield value++
    }
}
// while ํŒจํ„ด์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ์ƒ์„ฑ๊ธฐ
let iterator = rangeGenerator(1, 3+1)
while(1) {
    const {value, done} = iterator.next()
    if(done) break
    console.log(value) // 1 2 3
}

// for...of ํŒจํ„ด์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ์ƒ์„ฑ๊ธฐ(ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์ง€ ์•Š์•„๋„ of ๋’ค์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์Œ
for(let value of rangeGenerator(4, 6+1))
    console.log(value) // 5 6 7

 

๐Ÿ“ ๋ฐ˜๋ณต๊ธฐ ์ œ๊ณต์ž์˜ ๋ฉ”์„œ๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ์ƒ์„ฑ๊ธฐ ๊ตฌํ˜„

export class IterableUsingGenerator<T> implements Iterable<T> {
    constructor(private values: T{] = [], private currentIndex: number = 0) {}
    [Symbol.iterator] = function* () {
        while(this.currentIndex < this.values.length)
            yield this.values[this.currentIndex++]
    }
}

โžฐ ์ƒ์„ฑ๊ธฐ๋ฅผ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์˜ ๋ชธํ†ต์ด ๋˜๊ฒŒ ํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ [Symbol.iterator] = function* () ํ˜•์‹์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

 

๐Ÿ“ yield* ํ‚ค์›Œ๋“œ

โžฐ yield๋Š” ๋‹จ์ˆœํžˆ ๊ฐ’์„ ๋Œ€์ƒ์œผ๋กœ ๋™์ž‘ํ•˜์ง€๋งŒ, yield*์€ ๋‹ค๋ฅธ ์ƒ์„ฑ๊ธฐ๋‚˜ ๋ฐฐ์—ด์„ ๋Œ€์ƒ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

function* gen12(){
    yield 1
    yield 2
}

export function* gen12345() {
    yield* gen12()
    yield* [3, 4]
    yield 5
}
import {gen12345} from './yield-star'

for(let value of gen12345())
     console.log(value) // 1 2 3 4 5

โžฐ gen12345() ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด, yield* gen12() ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์„œ yield๋ฌธ์ด ๊ฐ’ 1์„ ์ƒ์„ฑํ•˜๊ณ  ์ฝ”๋“œ๋Š” ์ •์ง€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ for๋ฌธ์— ์˜ํ•ด yield* gen(12) ๊ฐ€ ์‹คํ–‰๋˜๊ณ , ์ •์ง€๊ฐ€ ํ’€๋ฆฌ๋ฉด์„œ ๊ฐ’ 2๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

๐Ÿ“ yield ๋ฐ˜ํ™˜๊ฐ’

export function* gen() {
    let count = 5
    let select = 0
    while(count--) {
        select = yield `you select ${select}`
    }
}

export const random = (max, min=0) => Math.rount(Math.random() * (max-min)) + min
import {random, gen} from './yield-return'
const iter = gen()
while(true) {
    const {value, done} = iter.next(random(10, 1))
    if(done) break
    console.log(value)
}

โžฐ yield ์—ฐ์‚ฐ์ž์˜ ๋ฐ˜ํ™˜๊ฐ’์€ ๋ฐ˜๋ณต๊ธฐ์˜ next ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๋•Œ ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ „๋‹ฌํ•˜๋Š” ๊ฐ’์ด๋‹ค.

โžฐ const {value, done} = iter.next(random(10, 1)) ๊ตฌ๋ฌธ์€ next ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๋•Œ ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด ์ „๋‹ฌํ•œ๋‹ค.