typescript - Tilldelning av objektspread till en array-typad variabel. Varför är det möjligt?


const myArray: string[] = {};

uppenbarligen klarar inte TypeScript-typkontrollen, men:

const iAmAnArrayTrustMe: string[] = {...[]} // it passes the type checking
iAmAnArrayTrustMe.splice(1)

klarar typkontrollen, men kraschar vid körning förstås

Jag förstår varför det kraschar vid körning, men jag förstår inte varför TypeScript tillåter den typen av tilldelning. Vad är anledningen bakom detta beteende?

Här är ett exempel att leka med: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAlgQQLYLAgTughgTwFwzTpxgDmA2gLowC8MA3gHTNUC+A3ALABQiKamXIwgAHADZxgAUwAUARgCU7IA

Svar #1

Jag tror att jag har fått det, och jag kommer att försöka dela mina förståelser baserat på insikterna från @jcalz i denna kommentar

Eftersom Array är definierad i TypeScript som ett gränssnitt, kan kompilatorn inte skilja vilka egenskaper som ägs (och kan listas) av den faktiska arrayinstansen. Så den antar att varje egenskap i gränssnittet kommer att kopieras under en Object.assign- eller spridningsoperation. Vilket inte är sant för metoder som 'splice'.

Ta det här gränssnittet som exempel:

interface IFoo {
  a: string,
  aFunction(): string;
  anotherFunction(): string;
}

Det finns inga skillnader mellan aFunction och anotherFunction. TS kan inte skilja dem åt.

Men du kan uppfylla det gränssnittet på olika sätt, som här:

class Foo implements IFoo {
  a = 'I am a string';
  aFunction () {
    return this.a;
  }
  anotherFunction = () => {
    return this.a;
  }
}

I denna Foo-klass är aFunction en metod i prototypkedjan. Medan anotherFunction är en anropbar egenskap, konstruerad och ägd av varje Foo-instans.

Kom ihåg att spridningsoperationen endast kommer att kopiera ägda uppräkningsbara egenskaper. Utdata av:

console.log(Object.getOwnPropertyNames({...new Foo()}));

kommer att vara:

["a", "anotherFunction"] 

Nu, med tanke på gränssnittet, klassen och vad vi vet om spridningsoperationen. Tänk på denna kod:

function myFunction(foo: IFoo) {
  const bar: IFoo = {...foo}
  bar.aFunction();
}

Enligt TS har både målet och källan för den tilldelningen tre medlemmar som är lika namngivna och av samma typ. Så det finns ingen anledning att kasta en varning eller ett fel.

Men om du kallar funktionen med new Foo() som argument:

myFunction(new Foo());

även när vi följer alla typer av kontroller, kommer spridningsoperationen inte att kopiera aFunction-medlemmen. Och den koden kommer att misslyckas vid körning

Här är en lekplats som förklarar fenomenet utan att använda arrayer.

Relaterade artiklar